使用F1C200S从零制作掌机之I2C传感器

访问I2C设备(比如eeprom),我知道的有三总方法:

(一)i2c-dev操作I2C设备:不用添加设备驱动,用户直接在应用层完成对具体I2C 设备的驱动工作。

(二)sysfs操作I2C设备:需添加设备驱动,通过sys展示出来的文件操作设备(比如/sys/devices/platform/s3c2440-i2c/i2c-0/0-0050/eeprom)

(三)设备节点操作i2C设备:添加设备驱动,为设备驱动创建设备节点,从/dev访问I2C设备(比如/dev/eeprom)

原文链接:https://blog.csdn.net/wuxiwang/article/details/140118183

一 OLED 12880(SH1107)

模组链接:https://item.taobao.com/item.htm?spm=a1z09.2.0.0.512d2e8dkifnvI\&id=674600863020\&_u=tom5bln21f7

模组资料:https://pan.baidu.com/s/17XWkG6xXvj94V_DfXcro8Q 1217

内核支持fb_sh1106.c,所以这里尝试按照去改fb_sh1106.c去适配fb_sh1107.c。

1.1 sh1106和sh1107的区别?

支持的分辨率不一样。

2.1 模块原理图

2.2 供电

5v供电。

2.3 接线

模块引脚 F1C200S
VCC 5V
GND GND
SDA TWI0_SDA/PD0
SCL TWI0_SCK/PD12

2.4 测试

sh1107_app.sh

i2cset -y 0 0x3c 0x00 0xAE
i2cset -y 0 0x3c 0x00 0xDC
i2cset -y 0 0x3c 0x00 0x00
i2cset -y 0 0x3c 0x00 0x20
i2cset -y 0 0x3c 0x00 0x81
i2cset -y 0 0x3c 0x00 0x80
i2cset -y 0 0x3c 0x00 0xA0
i2cset -y 0 0x3c 0x00 0xA4
i2cset -y 0 0x3c 0x00 0xA6
i2cset -y 0 0x3c 0x00 0xC0
i2cset -y 0 0x3c 0x00 0xA8
i2cset -y 0 0x3c 0x00 0x7F
i2cset -y 0 0x3c 0x00 0xD5
i2cset -y 0 0x3c 0x00 0x50
i2cset -y 0 0x3c 0x00 0xD3
i2cset -y 0 0x3c 0x00 0x00
i2cset -y 0 0x3c 0x00 0xDB
i2cset -y 0 0x3c 0x00 0x37
i2cset -y 0 0x3c 0x00 0xD9
i2cset -y 0 0x3c 0x00 0x22
i2cset -y 0 0x3c 0x00 0xaf

# 清屏
for i in {0..15}
do
        v=$((0xb0+i))
        i2cset -y 0 0x3c 0x00 $v
        i2cset -y 0 0x3c 0x00 0x11
        i2cset -y 0 0x3c 0x00 0x08

        for i in {0..79}
        do
                i2cset -y 0 0x3c 0x40 0xff
        done
done

# 黑屏
for i in {0..15}
do
        v=$((0xb0+i))
        i2cset -y 0 0x3c 0x00 $v
        i2cset -y 0 0x3c 0x00 0x11
        i2cset -y 0 0x3c 0x00 0x08

        for i in {0..79}
        do
                i2cset -y 0 0x3c 0x40 0x00
        done
done

二 温湿度传感器(SHT35)

内核已持支持的hwmon设备,查看https://www.kernel.org/doc/html/latest/hwmon/index.html

linux内核已自带的sht3x驱动,但是该驱动不支持设备树。

2.1 模块供电

2.2 硬件接线

模块引脚 F1C200S
VCC 3.3V
GND GND
SDA TWI0_SDA/PD0
SCL TWI0_SCK/PD12

2.3 内核配置

2.4 设备树

与之前保持一致。支持i2c0。

2.5 驱动

内核自带驱动增加调试代码。

vim driver/hwmon/sht3x.c
static int sht3x_probe(struct i2c_client *client,
		       const struct i2c_device_id *id)
{
	......

	// 在驱动匹配成功之后,会打印
	printk("sht3x_probe\r\n");
}

编译

sudo cp driver/hwmon/sht3x.ko /media/wang/rootfs/lib/modules/5.4.99

2.6 设备信息

sht35_device.c

#include <linux/types.h> 
#include <linux/kernel.h> 
#include <linux/module.h> 
#include <linux/errno.h> 
#include <linux/device.h> 
#include <linux/platform_device.h> 
#include <linux/i2c.h>

static struct i2c_board_info sht3x_i2c_board_info __initdata = {
	I2C_BOARD_INFO("sht3x", 0x44),
};

static int __init sht3x_init(void)
{
	int ret = 0;
	struct i2c_adapter *adapter;
	printk("sht3x_init\r\n");
	adapter = i2c_get_adapter(0);
	i2c_new_client_device(adapter, &sht3x_i2c_board_info);
	return ret;
}

static void __exit sht3x_exit(void) 
{ 
	
}
 
module_init(sht3x_init); 
module_exit(sht3x_exit); 
MODULE_LICENSE("GPL"); 
MODULE_AUTHOR("wangxinchen");

Makefile

ifneq ($(KERNELRELEASE),)
#kbuild syntax. dependency relationshsip of files and target modules are listed here.
obj-m := sht35_device.o
else
PWD:= $(shell pwd)

KDIR := /home/wang/workplace/code/linux-5.4.99
all:
	$(MAKE) -C $(KDIR) M=$(PWD) modules
	rm -rf .*.cmd *.o *.mod.c  .tmp_versions *.mod *.symvers *.order
clean:
	rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versions *.mod *.symvers *.order
endif

编译

make
sudo cp sht35_device.ko /media/wang/rootfs/lib/modules/5.4.99

2.7 测试

insmod /lib/modules/5.4.99/sht35_device.ko
insmod /lib/modules/5.4.99/sht3x.ko
ls /sys/class/hwmon/
ls /sys/class/hwmon/hwmon0
cat /sys/class/hwmon/hwmon0/temp1_input
cat /sys/class/hwmon/hwmon0/humidity1_input

2.8 应用程序

sht35_app.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main() {
    FILE *temp_file, *humi_file;
    int temperature, humidity;
    char buf[64];

    while(1){
	temp_file = fopen("/sys/class/hwmon/hwmon0/temp1_input", "r");
	if (temp_file == NULL) {
	    perror("Error opening file");
	    return EXIT_FAILURE;
	}
	humi_file = fopen("/sys/class/hwmon/hwmon0/humidity1_input", "r");
	if (temp_file == NULL) {
	    perror("Error opening file");
	    return EXIT_FAILURE;
	}

        // 读取温度值
        if (fgets(buf, sizeof(buf), temp_file) != NULL) {
            temperature = atoi(buf);
            printf("Temperature: %.3f C   ", (float)temperature / 1000); // 温度通常以毫摄氏度为单位
        }

        // 读取湿度
        if (fgets(buf, sizeof(buf), humi_file) != NULL) {
            humidity = atoi(buf);
            printf("Humidity: %.3f C\n", (float)humidity / 1000); // 温度通常以毫摄氏度为单位
        }

	fclose(temp_file);
	fclose(humi_file);
		
	//sleep(1);
	usleep(100000);  //100ms
    }

    return EXIT_SUCCESS;
}

需要放在移植的debian系统下编译。

gcc sht35_app.c -o sht35_app

导入开发板测试

创建I2C设备:
echo sht3x 0x44 > /sys/bus/i2c/devices/i2c-0/new_device
加载驱动
insmod /lib/modules/5.4.99/sht3x.ko
测试程序
./sht35_app
删除I2C设备:
echo 0x44 > /sys/bus/i2c/devices/i2c-0/delete_device

2.9 参考

https://blog.csdn.net/u014754841/article/details/132580947

https://blog.csdn.net/lihui126/article/details/45723017

三 气体传感器(SGP30)

3.1 供电

3.2 模块原理图

3.3 接线

模块 F1C200S
VCC 3.3V
GND GND
SDA TWI0_SDA/PD0
SCL TWI0_SCK/PD12

3.4 内核配置

3.5 设备树

	gas@58 {
		compatible = "sensirion,sgp30";
		reg = <0x58>;
		status = "okay";
	};

3.6 驱动

编译成模块,手动加载便于调试。

内核自带驱动使用了IIO子系统。

IIO 驱动框架提供了 sysfs 接口,因此加载成功以后我们可以在用户空间访问对应的 sysfs 目录项,进入目录"/sys/bus/iio/devices/"目录里面,此目录下都是 IIO 框架设备。

GP30主要是对空气质量进行检测,TVOC是一项重要指标,指总可挥发有机物气体。一般我们可以用它来反映甲醛的浓度!

SGP30气体传感器使用了MOS技术和电化学技术来检测VOC分子的浓度,并使用红外线吸收技术来检测CO2浓度。

sudo cp drivers/iio/chemical/sgp30.ko /media/wang/rootfs/lib/modules/5.4.99/

3.7 测试

insmod /lib/modules/5.4.99/sgp30.ko
驱动加载成功,在目录/sys/bus/iio/devices下可看到设备
ls /sys/bus/iio/devices
cat /sys/bus/iio/devices/iio\:device0/in_concentration_co2_input
cat /sys/bus/iio/devices/iio\:device0/in_concentration_voc_input

单位换算

在SGP30的测量中,‌二氧化碳(‌CO2)‌的浓度单位是ppm(‌parts per million)‌,‌而挥发性有机化合物(‌VOC)‌的浓度单位是ppb(‌parts per billion)‌。‌

二氧化碳浓度标准:多少PPM对人体有危害?

PPM: 气体浓度的100万分之一。※1%=10000ppm

PPB: 气体浓度的10亿分之一。※1ppm=1000ppb

这里读出来的结果:

0.000756 转换为 PPM --> 0.000756*1000000 = 756PPM

0.000000120 转换为 PPB --> 0.000000120*1000000000 = 120PPB = 0.12PPM

PPB转换为mg/L的公式是:‌1 ppb = 1 μg/L = 0.001 mg/L。‌

PPM转换为mg/L的公式是:‌1 ppm = 1 mg/L。

在室内空气中,‌VOC检测值的正常范围根据国家相关标准,‌一般在0-500μg/m³之间。‌

https://teesing.com/en/tools/ppm-mg3-converter

VOC 应小于0.5mg/m³(0.155 ppm)

二氧化碳浓度含量与人体的生理反应:

​ 1、350~450ppm,同一般室外环境;

​ 2、350~1000ppm,空气清新,呼吸顺畅;

​ 3、1000~2000ppm,感觉空气浑浊,并开始觉得昏昏欲睡。

3.8 应用程序

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

/*
	加载驱动
	insmod /lib/modules/5.4.99/sgp30.ko
	匹配成功目录/sys/bus/iio/devices下会有设备。
	
	VOC 应小于0.5mg/m³(0.155 ppm)
	
*/

int main() {
    FILE *voc_file, *co2_file;
    float voc_raw, co2_raw;
    char buf[64];

    while(1){
		voc_file = fopen("/sys/bus/iio/devices/iio\:device0/in_concentration_voc_input", "r");
		if (voc_file == NULL) {
			perror("Error opening file");
			return EXIT_FAILURE;
		}
		co2_file = fopen("/sys/bus/iio/devices/iio\:device0/in_concentration_co2_input", "r");
		if (co2_file == NULL) {
			perror("Error opening file");
			return EXIT_FAILURE;
		}

        if (fgets(buf, sizeof(buf), voc_file) != NULL) {
            voc_raw = atof(buf)*1000000000; // 单位PPB
        }
        if (fgets(buf, sizeof(buf), co2_file) != NULL) {
            co2_raw = atof(buf)*1000000; // 单位PPM
        }
        
        // 1PPM=1000PPB
        // 1PPB=1/1000PPM
        // voc Molecular Weight 78.9516 g/mol
        // 0.0409 x (ppm) x 78.9516 = 69.652
        
		printf("voc_mg:%.2f mg/m³ co2_ppm: %d\r\n", voc_raw/1000*0.0409*78.9516, (int)(co2_raw));
		
		fclose(voc_file);
		fclose(co2_file);
		
		//sleep(1);
		usleep(100000);  //100ms
    }

    return EXIT_SUCCESS;
}

四 加速度传感器(ADXL345)

4.1 模块原理图

支持I2C和SPI通信,是靠什么判断的?

4.2 供电

模块是5v供电,内部转3.3v。

4.3 接线

模块引脚 F1C200S
VCC50 5V
GND GND
SDA TWI0_SDA/PD0
SCL TWI0_SCK/PD12

4.4 配置内核

4.5 驱动

内核自带驱动,使用IIO子系统。IIO子系统参考正点原子Linux文档。

sudo cp drivers/iio/accel/adxl345_core.ko /media/wang/rootfs/lib/modules/5.4.99/
sudo cp drivers/iio/accel/adxl345_i2c.ko /media/wang/rootfs/lib/modules/5.4.99/

4.6 设备树

	accelerometer@53 {
        compatible = "adi,adxl345";
        reg = <0x53>;
		status = "okay";
    };

4.7 测试

IIO 驱动框架提供了 sysfs 接口,因此加载成功以后我们可以在用户空间访问对应的 sysfs 目录项,进入目录"/sys/bus/iio/devices/"目录里面,此目录下都是 IIO 框架设备。

insmod /lib/modules/5.4.99/adxl345_core.ko
insmod /lib/modules/5.4.99/adxl345_i2c.ko
驱动加载成功,在目录/sys/bus/iio/devices下可看到设备
ls /sys/bus/iio/devices
cat /sys/bus/iio/devices/iio\:device0/in_accel_scale
cat /sys/bus/iio/devices/iio\:device0/in_accel_x_raw
cat /sys/bus/iio/devices/iio\:device0/in_accel_y_raw
cat /sys/bus/iio/devices/iio\:device0/in_accel_z_raw

4.8 应用程序

如果不用设备树,可以
echo adxl345 0x53> /sys/bus/i2c/devices/i2c-0/new_device

adxl345_app.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

/*
	加载驱动
	insmod adxl345_core.ko
	insmod adxl345_i2c.ko
	
	匹配成功目录/sys/bus/iio/devices下会有设备。
*/

int main() {
    FILE *accel_scale_file, *accel_x_raw_file, *accel_y_raw_file, *accel_z_raw_file;
    int accel_x_raw, accel_y_raw, accel_z_raw;
	float accel_scale;
    char buf[64];

    while(1){
		accel_scale_file = fopen("/sys/bus/iio/devices/iio\:device0/in_accel_scale", "r");
		if (accel_scale_file == NULL) {
			perror("Error opening file");
			return EXIT_FAILURE;
		}
		accel_x_raw_file = fopen("/sys/bus/iio/devices/iio\:device0/in_accel_x_raw", "r");
		if (accel_x_raw_file == NULL) {
			perror("Error opening file");
			return EXIT_FAILURE;
		}
		accel_y_raw_file = fopen("/sys/bus/iio/devices/iio\:device0/in_accel_y_raw", "r");
		if (accel_y_raw_file == NULL) {
			perror("Error opening file");
			return EXIT_FAILURE;
		}
		accel_z_raw_file = fopen("/sys/bus/iio/devices/iio\:device0/in_accel_z_raw", "r");
		if (accel_z_raw_file == NULL) {
			perror("Error opening file");
			return EXIT_FAILURE;
		}

        if (fgets(buf, sizeof(buf), accel_scale_file) != NULL) {
            accel_scale = atof(buf);
            //printf("accel_scale: %f   ", accel_scale);
        }
        if (fgets(buf, sizeof(buf), accel_x_raw_file) != NULL) {
            accel_x_raw = atoi(buf);
            //printf("accel_x_raw: %d   ", accel_x_raw);
        }
		if (fgets(buf, sizeof(buf), accel_y_raw_file) != NULL) {
            accel_y_raw = atoi(buf);
            //printf("accel_y_raw: %d   ", accel_y_raw);
        }
		if (fgets(buf, sizeof(buf), accel_z_raw_file) != NULL) {
            accel_z_raw = atoi(buf);
            //printf("accel_z_raw: %d\n", accel_z_raw);
        }
		printf("accel x:%.2f y:%.2f z:%.2f\r\n", accel_scale*accel_x_raw, accel_scale*accel_y_raw, accel_scale*accel_z_raw);
		
		fclose(accel_scale_file);
		fclose(accel_x_raw_file);
		fclose(accel_y_raw_file);
		fclose(accel_z_raw_file);
		
		//sleep(1);
		usleep(100000);  //100ms
    }

    return EXIT_SUCCESS;
}

执行结果

gcc adxl345_app.c -o adxl345_app
root@wangpi:/home/wxc# ./adxl345_app
accel x:0.34 y:-8.04 z:-3.87
accel x:0.31 y:-8.08 z:-4.02
accel x:0.31 y:-8.08 z:-4.02
accel x:0.27 y:-8.08 z:-3.98
accel x:0.34 y:-8.04 z:-3.98
accel x:0.31 y:-8.04 z:-3.98
accel x:0.31 y:-8.08 z:-3.98
accel x:0.31 y:-8.08 z:-3.98

4.9 问题

accel_scale 为什么是0.0383???

在驱动源码adxl345_core.c中查到答案。

/*
 * In full-resolution mode, scale factor is maintained at ~4 mg/LSB
 * in all g ranges.
 *
 * At +/- 16g with 13-bit resolution, scale is computed as:
 * (16 + 16) * 9.81 / (2^13 - 1) = 0.0383
 */
static const int adxl345_uscale = 38300;

4.10 libiio库

sudo apt install libiio-dev

未完成。

4.11 参考

【【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.81】的IIO章节。

五、SSD1306

5.1 模组链接

5.2 模组原理图

5.3 供电

3.3v供电。

5.4 接线

模块引脚 F1C200S
VCC 3.3V
GND GND
SDA TWI0_SDA/PD0
SCL TWI0_SCK/PD12

5.5 测试

SSD1306_app.sh

i2cset -y 0 0x3c 0x00 0xAE
i2cset -y 0 0x3c 0x00 0x00
i2cset -y 0 0x3c 0x00 0x10
i2cset -y 0 0x3c 0x00 0x40
i2cset -y 0 0x3c 0x00 0x81
i2cset -y 0 0x3c 0x00 0xCF
i2cset -y 0 0x3c 0x00 0xA1
i2cset -y 0 0x3c 0x00 0xC8
i2cset -y 0 0x3c 0x00 0xA6
i2cset -y 0 0x3c 0x00 0xA8
i2cset -y 0 0x3c 0x00 0x3f
i2cset -y 0 0x3c 0x00 0xD3
i2cset -y 0 0x3c 0x00 0x00
i2cset -y 0 0x3c 0x00 0xd5
i2cset -y 0 0x3c 0x00 0x80
i2cset -y 0 0x3c 0x00 0xD9
i2cset -y 0 0x3c 0x00 0xF1
i2cset -y 0 0x3c 0x00 0xDA
i2cset -y 0 0x3c 0x00 0x12
i2cset -y 0 0x3c 0x00 0xDB
i2cset -y 0 0x3c 0x00 0x40
i2cset -y 0 0x3c 0x00 0x20
i2cset -y 0 0x3c 0x00 0x02
i2cset -y 0 0x3c 0x00 0x8D
i2cset -y 0 0x3c 0x00 0x14
i2cset -y 0 0x3c 0x00 0xA4
i2cset -y 0 0x3c 0x00 0xA6
i2cset -y 0 0x3c 0x00 0xAF

# 清屏
for i in {0..7}
do
		echo $i
        v=$((0xb0+i))
        i2cset -y 0 0x3c 0x00 $v
        i2cset -y 0 0x3c 0x00 0x00
        i2cset -y 0 0x3c 0x00 0x10

        for i in {0..127}
        do
                i2cset -y 0 0x3c 0x40 0xff
        done
done
# 黑屏
for i in {0..7}
do
		echo $i
        v=$((0xb0+i))
        i2cset -y 0 0x3c 0x00 $v
        i2cset -y 0 0x3c 0x00 0x00
        i2cset -y 0 0x3c 0x00 0x10

        for i in {0..127}
        do
                i2cset -y 0 0x3c 0x40 0x00
        done
done

5.6 内核配置

5.7 设备树

    ssd1306: oled@3c {
		compatible = "solomon,ssd1306fb-i2c";
		reg = <0x3c>;
		reset-gpios=<&pio 4 12 GPIO_ACTIVE_LOW>;
		solomon,height = <64>;
		solomon,width = <128>;
		solomon,page-offset = <0>;
		solomon,com-lrremap;
		solomon,com-invdir;
		//solomon,com-offset = <32>;
		status = "okay";
	};

5.8 驱动

使用drivers/video/fbdev/ssd1307fb.c

5.9 测试

insmod /lib/modules/5.4.99/ssd1307fb.ko
相关推荐
行思理9 分钟前
Linux 下SVN新手操作手册
linux·运维·svn
初学者丶一起加油20 分钟前
C语言基础:指针(数组指针与指针数组)
linux·c语言·开发语言·数据结构·c++·算法·visual studio
紫阡星影32 分钟前
【模块系列】STM32&1.69TFT屏幕
stm32·单片机·嵌入式硬件
彭某。36 分钟前
STM32低功耗模式结合看门狗
stm32·单片机·嵌入式硬件
憧憬一下1 小时前
PCI/PCIe设备INTx中断机制和MSI中断机制
arm开发·嵌入式硬件·嵌入式·linux驱动开发·pci/pcie
一只搬砖的猹1 小时前
cJson系列——常用cJson库函数
linux·前端·javascript·python·物联网·mysql·json
不能只会打代码1 小时前
32单片机综合案例——智能环境监控系统
单片机·嵌入式硬件
莫固执,朋友2 小时前
Linux下编译 libwebsockets简介和使用示例
linux·websocket·音视频
DCTANT2 小时前
【合作原创】使用Termux搭建可以使用的生产力环境(八)
linux·debian·idea·termux·vnc·xfce4·termux-x11
开疆智能2 小时前
ModbusTCP转Profinet:工业通信的利器
linux·服务器·网络