2995|5

48

帖子

0

TA的资源

一粒金砂(中级)

楼主
 

【微雪 RP2040双核开发板测试】——MAX30100血氧传感器 [复制链接]

【微雪 RP2040双核开发板测试】——MAX30100血氧传感器

 

《微雪 RP2040双核开发板》具有丰富的外部接口,给用户进行硬件开发提供了方便,上篇文章已经对硬件接口进行了介绍,本文主要介绍MAX0100血氧传感器的应用开发。

 

  • MAX0100血氧传感器介绍

Max30100是一款集成的脉搏血氧和心率检测传感器。采样集成化的两个LED灯设计,一个用来优化光学的光电探测器,和低噪声模拟信号处理器,提高了测量性能,可用来检测脉搏的血氧和心率信号。

Max30100的运行电压在1.8V到3.3V之间,并且可以通过软件来控制,待机电流极小,可以忽略不计,超低功耗操作增加电池寿命,适合可穿戴设备。

具体性能参数如下:

1、引脚定义

 

 

2、MAX30100内部功能框图

 

 

3、寄存器图表及其描述

 

各寄存器介绍:

  • 中断状态寄存器(基地址0x00)

 

总共有5个中断状态,每个中断状态控制都是一样的:高电平的相关中断使能,直到中断被清除,中断才停止。

Bit7~ Bit0各位的具体意义不再赘述,可参考产品数据手册。

  • 中断使能寄存器(基地址0x01)

 

在MAX30100 IC里,每一个硬件中断的来源都可以使用软件寄存器来控制其不使能状态,除了电源准备完成中断。开机中断不能被打断,因为在MAX30100复位时,默认状态下是不使能的。

当中断使能位被设置成0的时候,相应的中断在其寄存器中是1(上一个介绍),但是INT引脚不是低电平(相反电平)。(B3:B0设置为0)

  • FIFO寄存器(基地址 0x02—0x05)

 

①、FIFO写指针寄存器(FIFO Write Pointer)(FIFO_WR_PTR基地址 0x02)

FIFO写指针指向MAX30100写的下一个数据或命令的位置。这个指针把每一个数据或者命令放进FIFO中。当 MOD[2:0]被设置的时候,也可以通过IIC总线的方式进行改变。

②、FIFO溢出计数器寄存器(OVF_COUNTER基地址 0x03)

当FIFO寄存器的数据记满数据,采样的数据将溢出FIFO寄存器,并且数据将会丢失。OVF_COUNTER 会保存溢出的数据。保存在0xF中。当所有的数据从FIFO中取出,OVF_COUNTER就会被置零。

③、FIFO 读指针寄存器(FIFO_RD_PTR基地址 0x04)

FIFO读指针指向处理器通过IIC总线从FIFO通道获取的下一个数据。每次只从FIFO取出一个数据。当在读数据的时候,控制器也可以使用这个寄存器来写命令或者数据到FIFO中,如果FIFO的数据传输出现错误,也可以重新从其读出样本。

④、FIFO数据寄存器(FIFO_DATA基地址 0x05)

循环的FIFO是16位的数据存储,能够存储16个SPO2通道数据(Red 和 IR)。FIFO_DATA寄存器在IIC寄存器的映射下指向从FIFO中读出的下一个数据。FIFO_RD_PTR(FIFO读数据指针)指向这个数据。FIFO_DATA寄存器不会自动增加其地址值,因此会反复读取这个地址的数据。每一个样本是4字节的数据,因此得到一个样本需要读取4次的FIFO_DATA寄存器。理论上上面的所有寄存器都可以进行读取或者写入数据,但是在实际上,只有FIFO_RD_PTR寄存器才可以进行写入操作。其他的寄存器的数据只能自动增加或者由MAX30100进行填充(填满)。当开始一个新的SPO2或者心率转换,我们希望FIFO_RD_PTR、OVF_COUNTE、FIFO_WR_PTR寄存器能够被清零,以确保FIFO是空并且是已知的状态。当从IIC读取MAX30100寄存器时,地址指针寄存器就会自动的增加,那么读取的字节就是下一个寄存器发送的。FIFO_DATA寄存器不是这样的,他的指针不会增加。在FIFO数据寄存器中下一个被发送的数据就是下一个可用的数据。

  • 关于FIFO数据结构

FIFO的数据存储器由16个IR和RED的ADC数据样本存储块组成。每一个样本由一个IR字和一个RED字,总共有4个这样的字节数据组成。因此FIFO数据由4*16=64字节的数据组成。

每一个样本数据由一个IR和一个RED数据字(2个寄存器)组成,因此每次读取一个样本,需要4个IIC字节数据读一行。当4字节样本被读取完毕,FIFO的读指针就会自动增加。

在心率模式下,每个样本的第3和第4字节会被置0,但是其他的FIFO配置都是一样的。

  • MAX30100与RP2040的连接

由于MAX30100血氧传感器常用I2C总线接口,分别为VCC、GND、SCL、SDA和RED、IR灯的电压调节输入端、中断信号,本项目仅使用I2C数据通信接口,其他开支端不使用,悬空处理。VCC、GND、SDA、SCL分别接RP2040的GPIO引出排母H1的第18、20、1(GPIO 8)、3(GPIO 9)即可。

  • 运行
  1. 连接MAX30100血氧传感器
  2. MAX30100demo.py

import max30100

mx30 = max30100.MAX30100()

mx30.refresh_temperature()

temp = mx30.get_temperature()

print('TEMP=%d'%temp)

reg = mx30.get_registers()

print(reg)

mx30.enable_spo2()

aa=mx30.read_sensor()

 

mx30.ir, mx30.red

print(mx30.buffer_red[-10:])

 

  1. MAX30100数据采集程序MAX30100.py如下:

""""

  MicroPythonb Library for the Maxim MAX30100 pulse oximetry system

  """

 

from machine import Pin,I2C

 

INT_STATUS   = 0x00  # Which interrupts are tripped

INT_ENABLE   = 0x01  # Which interrupts are active

FIFO_WR_PTR  = 0x02  # Where data is being written

OVRFLOW_CTR  = 0x03  # Number of lost samples

FIFO_RD_PTR  = 0x04  # Where to read from

FIFO_DATA    = 0x05  # Ouput data buffer

MODE_CONFIG  = 0x06  # Control register

SPO2_CONFIG  = 0x07  # Oximetry settings

LED_CONFIG   = 0x09  # Pulse width and power of LEDs

TEMP_INTG    = 0x16  # Temperature value, whole number

TEMP_FRAC    = 0x17  # Temperature value, fraction

REV_ID       = 0xFE  # Part revision

PART_ID      = 0xFF  # Part ID, normally 0x11

 

I2C_ADDRESS  = 0x57  # I2C address of the MAX30100 device

 

 

PULSE_WIDTH = {

    200: 0,

    400: 1,

    800: 2,

   1600: 3,

}

 

SAMPLE_RATE = {

    50: 0,

   100: 1,

   167: 2,

   200: 3,

   400: 4,

   600: 5,

   800: 6,

  1000: 7,

}

 

LED_CURRENT = {

       0: 0,

     4.4: 1,

     7.6: 2,

    11.0: 3,

    14.2: 4,

    17.4: 5,

    20.8: 6,

    24.0: 7,

    27.1: 8,

    30.6: 9,

    33.8: 10,

    37.0: 11,

    40.2: 12,

    43.6: 13,

    46.8: 14,

    50.0: 15

}

 

def _get_valid(d, value):

    try:

        return d[value]

    except KeyError:

        raise KeyError("Value %s not valid, use one of: %s" % (value, ', '.join([str(s) for s in d.keys()])))

 

def _twos_complement(val, bits):

    """compute the 2's complement of int value val"""

    if (val & (1 << (bits - 1))) != 0: # if sign bit is set e.g., 8bit: 128-255

        val = val - (1 << bits)

    return val

 

INTERRUPT_SPO2 = 0

INTERRUPT_HR = 1

INTERRUPT_TEMP = 2

INTERRUPT_FIFO = 3

MODE_HR = 0x02

MODE_SPO2 = 0x03

class MAX30100(object):

 

    def __init__(self,

                 i2c=None,

                 mode=MODE_HR,

                 sample_rate=100,

                 led_current_red=11.0,

                 led_current_ir=11.0,

                 pulse_width=1600,

                 max_buffer_len=10000

                 ):

        self.i2c =I2C(id=0,scl=Pin(9),sda=Pin(8),freq=100_000)

        self.set_mode(MODE_HR)  # Trigger an initial temperature read.

        self.set_led_current(led_current_red, led_current_ir)

        self.set_spo_config(sample_rate, pulse_width)

 

        # Reflectance data (latest update)

        self.buffer_red = []

        self.buffer_ir = []

 

        self.max_buffer_len = max_buffer_len

        self._interrupt = None

 

    @property

    def red(self):

        return self.buffer_red[-1] if self.buffer_red else None

 

    @property

    def ir(self):

        return self.buffer_ir[-1] if self.buffer_ir else None

 

    def set_led_current(self, led_current_red=11.0, led_current_ir=11.0):

        # Validate the settings, convert to bit values.

        led_current_red = _get_valid(LED_CURRENT, led_current_red)

        led_current_ir = _get_valid(LED_CURRENT, led_current_ir)

        self.write_byte_data(I2C_ADDRESS, LED_CONFIG, chr((led_current_red << 4) | led_current_ir))

   

    def set_mode(self, mode):

        reg = self.read_byte_data(I2C_ADDRESS, MODE_CONFIG)

        self.write_byte_data(I2C_ADDRESS, MODE_CONFIG, chr(reg & 0x74))# mask the SHDN bit

        self.write_byte_data(I2C_ADDRESS, MODE_CONFIG, chr(reg | mode))

       

    def set_spo_config(self, sample_rate=100, pulse_width=1600):

        reg = self.read_byte_data(I2C_ADDRESS, SPO2_CONFIG)

        reg = reg & 0xFC  # Set LED pulsewidth to 00

        self.write_byte_data(I2C_ADDRESS, SPO2_CONFIG, chr(reg | pulse_width))

 

    def read_byte_data(self, i2c_addr, addr):

        bval =  self.i2c.readfrom_mem(i2c_addr, addr, 1)

        return bval[0] 

    def read_bytes_data(self, i2c_addr, addr, len = 1):

        bval =  self.i2c.readfrom_mem(i2c_addr, addr, len)

        return bval

    def write_byte_data(self, i2c_addr, addr, chs):

        self.i2c.writeto_mem(i2c_addr,addr, chs)

 

    def enable_spo2(self):

        self.set_mode(MODE_SPO2)

 

    def disable_spo2(self):

        self.set_mode(MODE_HR)

 

    def enable_interrupt(self, interrupt_type):

       

        self.write_byte_data(I2C_ADDRESS, INT_ENABLE, chr((interrupt_type + 1)<<4))

       

        self.read_byte_data(I2C_ADDRESS, INT_STATUS)

 

    def get_number_of_samples(self):

        write_ptr = self.read_byte_data(I2C_ADDRESS, FIFO_WR_PTR)

        read_ptr = self.read_byte_data(I2C_ADDRESS, FIFO_RD_PTR)

        return abs(16+write_ptr - read_ptr) % 16

 

    def read_sensor(self):

        #bytes = self.i2c.read_i2c_block_data(I2C_ADDRESS, FIFO_DATA, 4)

        bytes = self.read_bytes_data(I2C_ADDRESS, FIFO_DATA, 4)

        # Add latest values.

        self.buffer_ir.append(bytes[0]<<8 | bytes[1])

        self.buffer_red.append(bytes[2]<<8 | bytes[3])

        # Crop our local FIFO buffer to length.

        self.buffer_red = self.buffer_red[-self.max_buffer_len:]

        self.buffer_ir = self.buffer_ir[-self.max_buffer_len:]

 

    def shutdown(self):

        reg = self.read_byte_data(I2C_ADDRESS, MODE_CONFIG)

        self.write_byte_data(I2C_ADDRESS, MODE_CONFIG, chr(reg | 0x80))

   

    def reset(self):

        reg = self.read_byte_data(I2C_ADDRESS, MODE_CONFIG)

        self.write_byte_data(I2C_ADDRESS, MODE_CONFIG, chr(reg | 0x40))

        

    def refresh_temperature(self):

        reg = self.read_byte_data(I2C_ADDRESS, MODE_CONFIG)

        self.write_byte_data(I2C_ADDRESS, MODE_CONFIG, chr(reg | (1 << 3)))

       

    def get_temperature(self):

        #intg = _twos_complement(self.read_byte_data(I2C_ADDRESS, TEMP_INTG))

        intg = self.read_byte_data(I2C_ADDRESS, TEMP_INTG)

        frac = self.read_byte_data(I2C_ADDRESS, TEMP_FRAC)

        return intg + (frac * 0.0625)

 

    def get_rev_id(self):

        return self.read_byte_data(I2C_ADDRESS, REV_ID)

      

    def get_part_id(self):

        return self.read_byte_data(I2C_ADDRESS, PART_ID)

       

    def get_registers(self):

        return {

            "INT_STATUS": self.read_byte_data(I2C_ADDRESS, INT_STATUS),

            "INT_ENABLE": self.read_byte_data(I2C_ADDRESS, INT_ENABLE),

            "FIFO_WR_PTR": self.read_byte_data(I2C_ADDRESS, FIFO_WR_PTR),

            "OVRFLOW_CTR": self.read_byte_data(I2C_ADDRESS, OVRFLOW_CTR),

            "FIFO_RD_PTR": self.read_byte_data(I2C_ADDRESS, FIFO_RD_PTR),

            "FIFO_DATA": self.read_byte_data(I2C_ADDRESS, FIFO_DATA),

            "MODE_CONFIG": self.read_byte_data(I2C_ADDRESS, MODE_CONFIG),

            "SPO2_CONFIG": self.read_byte_data(I2C_ADDRESS, SPO2_CONFIG),

            "LED_CONFIG": self.read_byte_data(I2C_ADDRESS, LED_CONFIG),

            "TEMP_INTG": self.read_byte_data(I2C_ADDRESS, TEMP_INTG),

            "TEMP_FRAC": self.read_byte_data(I2C_ADDRESS, TEMP_FRAC),

            "REV_ID": self.read_byte_data(I2C_ADDRESS, REV_ID),

            "PART_ID": self.read_byte_data(I2C_ADDRESS, PART_ID),

        }

 

 

  1. 运行结果:

 

由于RP2040的I2C0引脚8,9已经用作屏幕的CS和DC引脚,故这里我们只能选择PRINT语句输出数据。

 

  • 总结

本文未对测量的数据做进一步的处理,要精确得到血液含氧量数据需要进行FFT处理,由于需要处理的数据量较大,且需要使用到micropython的数据处理模块,这里不做进一步处理。如读者需要进行数据处理和查阅相关资料。

此帖出自消费电子论坛

最新回复

现在血氧这个太应景了。:)   详情 回复 发表于 2022-12-26 10:08
点赞 关注
 
 

回复
举报

4942

帖子

12

TA的资源

版主

沙发
 

期待成品 血氧蛮关键的对于新冠这种小传染病来说

此帖出自消费电子论坛
 
 
 

回复

1万

帖子

25

TA的资源

版主

板凳
 

在加上心率就是一个完整产品了

此帖出自消费电子论坛
 
 
 

回复

6828

帖子

0

TA的资源

五彩晶圆(高级)

4
 

MAX30100血氧传感器看来可以试用一下

此帖出自消费电子论坛
 
 
 

回复

6561

帖子

9

TA的资源

版主

5
 

文章后边是不是少了部分东西

此帖出自消费电子论坛
个人签名

在爱好的道路上不断前进,在生活的迷雾中播撒光引

 
 
 

回复

2万

帖子

74

TA的资源

管理员

6
 

现在血氧这个太应景了。:)

此帖出自消费电子论坛
加EE小助手好友,
入技术交流群
EE服务号
精彩活动e手掌握
EE订阅号
热门资讯e网打尽
聚焦汽车电子软硬件开发
认真关注技术本身
个人签名

加油!在电子行业默默贡献自己的力量!:)

 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/6 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表