【RV1126】IIC驱动--MAX30102

文章目录

  • 实物照片
  • 模块简介
  • 设备树
  • 设备驱动
  • 主机驱动
    • 编译脚本
  • 应用层测试程序
  • 测试

实物照片

【RV1126】IIC驱动--MAX30102_第1张图片

模块简介

MAX30102是一个集成的脉搏血氧仪和心率监测仪生物传感器的模块。

它集成了一个红光LED和一个红外光LED、光电检测器、光器件,以及带环境光抑制的低噪声电子电路。

MAX30102采用一个1.8V电源和一个独立的5.0V用于内部LED的电源,应用于可穿戴设备进行心率和血氧采集检测,佩戴于手指点耳垂和手腕处。

标准的I2C兼容的通信接口可以将采集到的数值传输给Arduino、KL25Z、STM32、STC51等单片机进行心率和血氧计算。

此外,该芯片还可以通过软件关断模块,待机电流接近为零,实现电源始终维持供电状态。

【RV1126】IIC驱动--MAX30102_第2张图片【RV1126】IIC驱动--MAX30102_第3张图片

设备树

&i2c4 {
	status = "okay";
	eeprom@50 {
		status = "okay";
		compatible = "atmel,at24c256B";
		reg = <0x50>;
	};
	maxim@57 {
		status = "okay";
		compatible = "maxim,max30102";
		reg = <0x57>;
	};
};

使用了I2C4这个外设,然后再这个总线上挂了两个IIC设备:EEPROM和Max30102。

设备驱动

iic_dev.c

#include 
#include 
#include 
#include 
#include 
 
static struct i2c_client *client = NULL;
static struct i2c_adapter *adapter = NULL;

static struct i2c_board_info max30102_info =
{
	I2C_BOARD_INFO("rv1126_max30102", 0x57),
	//.irq = ,
};
 
static int __init rv1126_dev_init(void)
{	
	/*根据总线编号获取是适配器*/
	adapter = i2c_get_adapter(0);
	/*注册IIC设备端*/
	client = i2c_new_device(adapter, &max30102_info);

    printk("IIC设备端: 驱动安装成功\n");
    return 0;
}
 
static void __exit rv1126_dev_cleanup(void)
{
	/*注销IIC设备*/
	i2c_unregister_device(client);

	i2c_put_adapter(adapter);

    printk("IIC设备端: 驱动卸载成功\n");
}
 
module_init(rv1126_dev_init);    /*驱动入口--安装驱动的时候执行*/
module_exit(rv1126_dev_cleanup); /*驱动出口--卸载驱动的时候执行*/

MODULE_AUTHOR("liefyuan");
MODULE_LICENSE("GPL");  /*设置模块的许可证--GPL*/
MODULE_DESCRIPTION("rv1126 MAX30102 Driver");

主机驱动

iic_drv.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

//max30102 register addresses
#define REG_INTR_STATUS_1   0x00
#define REG_INTR_STATUS_2   0x01
#define REG_INTR_ENABLE_1   0x02
#define REG_INTR_ENABLE_2   0x03
#define REG_FIFO_WR_PTR     0x04
#define REG_OVF_COUNTER     0x05
#define REG_FIFO_RD_PTR     0x06
#define REG_FIFO_DATA       0x07
#define REG_FIFO_CONFIG     0x08
#define REG_MODE_CONFIG     0x09
#define REG_SPO2_CONFIG     0x0A
#define REG_LED1_PA         0x0C
#define REG_LED2_PA         0x0D
#define REG_PILOT_PA        0x10
#define REG_MULTI_LED_CTRL1 0x11
#define REG_MULTI_LED_CTRL2 0x12
#define REG_TEMP_INTR       0x1F
#define REG_TEMP_FRAC       0x20
#define REG_TEMP_CONFIG     0x21
#define REG_PROX_INT_THRESH 0x30
#define REG_REV_ID          0xFE
#define REG_PART_ID         0xFF
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 
static struct i2c_client *maxim_client;

static int max30102_write_reg(u8 reg, u8 val, u8 len)
{ 
    u8 buff[256];
    struct i2c_msg msg;
 
    buff[0] = reg;                    /* 寄存器首地址 */
 
    memcpy(&buff[1], &val, 1);        /* 将要写入的数据拷贝到数组b里面 */
 
    msg.addr = maxim_client->addr;    /* max30102地址 */
    msg.flags = 0;                /* 标记为写数据 */
    msg.buf = buff;                /* 要写入的数据缓冲区 */
    msg.len = len + 1;            /* 要写入的数据长度 */
 
    return i2c_transfer(maxim_client->adapter, &msg, 1);
}

static int max30102_read_reg(u8 reg, void *val, u8 len)
{
    int ret;
    struct i2c_msg msg[2];

    /* msg[0]为发送要读取的首地址 */
    msg[0].addr = maxim_client->addr;    /* max30102地址 */
    msg[0].flags = 0;                    /* 标记为发送数据 */
    msg[0].buf = &reg;                   /* 读取的首地址 */
    msg[0].len = 1;                      /* reg长度*/

    /* msg[1]读取数据 */
    msg[1].addr = maxim_client->addr;   /* max30102地址 */
    msg[1].flags = I2C_M_RD;            /* 标记为读取数据*/
    msg[1].buf = val;                   /* 读取数据缓冲区 */
    msg[1].len = len;                   /* 要读取的数据长度*/
 
    ret = i2c_transfer(maxim_client->adapter, msg, 2);
 
    if(ret == 2) {
        ret = 0;
    } 
    else 
    {
        printk("i2c rd failed=%d reg=%06x len=%d\n",ret, reg, len);
        ret = -EREMOTEIO;
    }
    return ret;
}

static void max30102_init(void)
{
	max30102_write_reg(REG_INTR_ENABLE_1, 0xc0, 1);  // INTR setting

	max30102_write_reg(REG_INTR_ENABLE_2, 0x00, 1);

	max30102_write_reg(REG_FIFO_WR_PTR, 0x00, 1);  //FIFO_WR_PTR[4:0]

	max30102_write_reg(REG_OVF_COUNTER, 0x00, 1);  //OVF_COUNTER[4:0]

	max30102_write_reg(REG_FIFO_RD_PTR, 0x00, 1);  //FIFO_RD_PTR[4:0]

	max30102_write_reg(REG_FIFO_CONFIG, 0x0f, 1);  //sample avg = 1, fifo rollover=false, fifo almost full = 17

	max30102_write_reg(REG_MODE_CONFIG, 0x03, 1);   //0x02 for Red only, 0x03 for SpO2 mode 0x07 multimode LED

	max30102_write_reg(REG_SPO2_CONFIG, 0x27, 1);  // SPO2_ADC range = 4096nA, SPO2 sample rate (100 Hz), LED pulseWidth (400uS)

	max30102_write_reg(REG_LED1_PA, 0x24, 1);  // Choose value for ~ 7mA for LED1

	max30102_write_reg(REG_LED2_PA, 0x24, 1);  // Choose value for ~ 7mA for LED2

	max30102_write_reg(REG_PILOT_PA, 0x7f, 1);  // Choose value for ~ 25mA for Pilot LED
}

static u8 fifo_buff[6];
static int rv1126_open(struct inode *inode, struct file *file)
{
	printk("rv1126_open-->ok\n");
	
	max30102_init();
	
	printk("max30102 init -->ok\n");
	
	return 0;
}
 
static ssize_t rv1126_read(struct file *file, char __user *buf, size_t size, loff_t *seek)
{	
	unsigned long err;

    max30102_read_reg(REG_FIFO_DATA, fifo_buff, 6);
	
	size = sizeof(fifo_buff);

	err = copy_to_user(buf, fifo_buff, size);
	
	if(err != 0)
		return -1;
	
	return size;
}
 
static ssize_t rv1126_write(struct file *file, const char __user *buf, size_t size, loff_t *seek)
{
	return 0;
}
 
static int rv1126_release(struct inode *inode, struct file *file)
{
	printk("rv1126_release-->ok\n");
	return 0;
}
 
static struct file_operations fops=
{
	.open = rv1126_open,
	.read = rv1126_read,
	.write = rv1126_write,
	.release = rv1126_release,
};
 
/*
Linux内核管理驱动---设备号
设备号是一个unsigned int 的变量--32位。
设备号=主设备号+次设备号
*/
static struct miscdevice misc=
{
	.minor = MISC_DYNAMIC_MINOR,  /*次设备号填255表示自动分配     主设备号固定为10*/
	.name = "rv1126_max30102",  /*/dev目录下文件名称*/
	.fops = &fops, /*文件操作接口*/
};
 
 
static int rv1126_probe(struct i2c_client *client, const struct i2c_device_id *device_id)
{
	printk("probe调用成功:%#X\n",client->addr);
	maxim_client = client;
	
	/*1. 杂项设备的注册函数*/
	misc_register(&misc);
	
	return 0;
}
 
static int rv1126_remove(struct i2c_client *client)
{
	/*2. 杂项设备的注销函数*/
	misc_deregister(&misc);
	printk("remove调用成功.\n");
	return 0;
}

static const struct of_device_id max30102_of_match[] = {
	{ .compatible = "maxim,max30102" },
	{}
};

static struct i2c_driver driver =
{
	.probe = rv1126_probe,
	.remove = rv1126_remove,
	.driver =
	{
		.name = "max30102",
		.of_match_table = of_match_ptr(max30102_of_match),
	},
};
 
static int __init rv1126_drv_init(void)
{	
	/*注册IIC驱动端*/
	i2c_add_driver(&driver);
    printk("IIC驱动端: 驱动安装成功\n");
    return 0;
}
 
static void __exit rv1126_drv_cleanup(void)
{
	/*注销IIC驱动端*/
	i2c_del_driver(&driver);
    printk("IIC驱动端: 驱动卸载成功\n");
}
 
module_init(rv1126_drv_init);    /*驱动入口--安装驱动的时候执行*/
module_exit(rv1126_drv_cleanup); /*驱动出口--卸载驱动的时候执行*/
 
MODULE_AUTHOR("liefyuan");
MODULE_LICENSE("GPL");  /*设置模块的许可证--GPL*/
MODULE_DESCRIPTION("rv1126 MAX30102 Driver");

编译脚本

Makfile

KER_DRI:=/home/liefyuan/rv1126/rp_rv1126_sdk/kernel/

# 定义当前目录
PWD:=$(shell pwd)

all:
	make -C $(KER_DRI) M=$(PWD) modules

obj-m += iic_dev.o
obj-m += iic_drv.o

clean:
	rm -rf *.order *o *.symvers *.mod.c *.mod *.ko
make ARCH=arm

应用层测试程序

app.c

#include 
#include 
#include 
#include 
#include 
#include 

#define MAX30102_DEV "/dev/rv1126_max30102"

unsigned char user_data[6];
uint32_t red_data = 0;
uint32_t ir_data = 0;

int main(int argc,char **argv)
{
    int cnt = 0;
    /*1. 打开设备文件*/
    int fd = open(MAX30102_DEV, O_RDWR);
    if(fd<0)
    {
        printf("%s 设备驱动打开失败.\n",MAX30102_DEV);
        return 0;
	}
    /*3.读写数据*/
	while(1)
    {
        cnt = read(fd, user_data, 6);

        red_data += (uint32_t)(user_data[0]<<16);
        red_data += (uint32_t)(user_data[1]<<8);
        red_data += (uint32_t)(user_data[2]);
        red_data &= 0x03FFFF;//Mask MSB [23:18]

        ir_data += (uint32_t)(user_data[3]<<16);
        ir_data += (uint32_t)(user_data[4]<<8);
        ir_data += (uint32_t)(user_data[5]);
        ir_data &= 0x03FFFF;//Mask MSB [23:18]

        printf("%d, %d\n", red_data, ir_data);
        red_data = 0;
        ir_data = 0;

        usleep(10000);      
    }
    return 0;
}

编译:

arm-linux-gnueabihf-gcc app.c

测试

安装模块:

[root@RV1126_RV1109:/mnt/nfs]# insmod iic_dev.ko
[ 4639.709410] IIC设备端: 驱动安装成功
[root@RV1126_RV1109:/mnt/nfs]# insmod iic_drv.ko
[ 4647.940402] probe调用成功:0X57
[ 4647.941534] IIC驱动端: 驱动安装成功

应用程序测试:

[root@RV1126_RV1109:/mnt/nfs]# ./a.out
[ 4709.332363] rv1126_open-->ok
[ 4709.336869] max30102 init -->ok
3627, 2908
3621, 2898
3616, 2907
3622, 2918
3622, 2921
3612, 2907
3616, 2902
3666, 2919
3621, 2887
3622, 2905
3610, 2917
3626, 2910
3608, 2914
3616, 2903
3578, 2905
3617, 2875
3621, 2916
3616, 2914
3614, 2913
^C[ 4710.450493] rv1126_release-->ok

你可能感兴趣的:(RV1126,嵌入式Linux驱动,嵌入式硬件)