公司给安排的小任务,最终效果和代码在文末。
硬件选型:MCU使用STM32F103C8T6
心率传感器使用MAX30100
屏幕选用1.8寸lcd屏,驱动型号ST7735
通讯接口:MCU->MAX30100使用软件模拟的IIC通讯
MCU->ST7735使用软件模拟SPI通讯
基础知识:IIC通讯:
IIC(Inter-Integrated Circuit)总线是一种由PHILIPS公司在80年代开发的两线式串行总线,用于连接微控制器及其外围设备。它是半双工通信方式。在STM32中自带的有IIC接口,大那是为了避免专利问题,ST公司将他的接口设计的非常复杂,更重要的是他不稳定,所以不推荐使用,一般直接软件模拟即可,还方便移植。
IIC总线拓扑图(因为总线接有上拉电阻,所以软件模拟IIC接口时需要把IO口设置为开漏输出模式):
IIC协议:
关于协议大家需要先了解几个点分别是空闲状态、起始条件、结束条件、应答与非应答、数据有效性。
空闲状态:当IIC总线的数据线SDA和时钟线SCL两条信号线同时处于高电平时,规定为总线的空闲状态。
起始信号:当时钟线SCL为高期间,数据线SDA由高到低的跳变;启动信号是一种电平跳变时序信号,而不是一个电平信号;
停止信号:当时钟线SCL为高期间,数据线SDA由低到高的跳变;停止信号也是一种电平跳变时序信号,而不是一个电平信号。
起始停止信号图
应答信号:发送器每发送一个字节(8个bit),就在时钟脉冲9期间释放数据线,由接收器反馈一个应答信号。
应答信号为低电平时,规定为有效应答位(ACK,简称应答位),表示接收器已经成功地接收了该字节;
应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功
应答信号图示
对于反馈有效应答位ACK的要求是:接收器在第9个时钟脉冲之前的低电平期间将数据线SDA拉低,并且确保在该时钟的高电平期间为稳定的低电平。 如果接收器是主控器,则在它收到最后一个字节后,发送一个NACK信号,以通知被控发送器结束数据发送,并释放数据线SDA,以便主控接收器发送一个停止信号P。
数据有效性:
IIC总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定;只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。
即:数据在时钟线SCL的上升沿到来之前就需准备好。并在在下降沿到来之前必须稳定。
IIC数据传输:
IIC总线上的每一个设备都可以作为主设备或者从设备,而且每一个设备都会对应一个唯一的地址(地址通过物理接地或者拉高),主从设备之间就通过这个地址来确定与哪个器件进行通信,在通常的应用中,我们把CPU带I2C总线接口的模块作为主设备,把挂接在总线上的其他设备都作为从设备。
也就是说,主设备在传输有效数据之前要先指定从设备的地址,地址指定的过程和上面数据传输的过程一样,只不过大多数从设备的地址是7位的,然后协议规定再给地址添加一个最低位用来表示接下来数据传输的方向,0表示主设备向从设备写数据,1表示主设备向从设备读数据。
MAX30100心率血氧传感器使用:
工作原理:
光电容积脉搏波描记法PPG:PPG传感器中的LED发射绿光穿过皮肤中的组织和动脉静脉,并被吸收和反射回到光电二极管PD中。像肌肉、骨骼、静脉和其他连接组织等对光的吸收是基本不变的(前提是测量部位没有大幅度的运动),但是血液不同,由于动脉里有血液的流动,那么对光的吸收自然也有所变化。当我们把光转换成电信号,正是由于动脉对光的吸收有变化而其他组织光的吸收基本不变,得到的信号就可以分为直流信号和交流信号。提取其中的交流信号,就能反应出血液流动的特点。
使用方法(摘自参考手册):
通讯方面:使用IIC通讯
首先是通讯上的一个时间要求,时钟信号高低电平时间,最小是1.3s和0.6us,数据持续时间是0~900ns
相关寄存器:
下图是MAX30100的完整寄存器描述,接下来我们按顺序对其进行的解释。
寄存器描述和映射
状态寄存器:
中断状态寄存器(Interrupt Status ):
寄存器一共八位:总共有5个中断状态,每个中断状态控制都是一样的:高电平的相关中断使能,直到中断被清除,中断才停止。无论什么时候,只要中断寄存器被读或者中断寄存器被触发,中断都会被清除。例如:如果SpO2传感器被触发当转换完成,读到FIFO的数据寄存器或者中断引脚的中断寄存器被清除,中断状态寄存器会被清除,中断寄存器为0.
Bit7:FIFO数据填满了的标志(A_FULL)
在SpO2和心率的模式下,当FIFO的写指针和读指针都被减去一时就会被触发,这也是说明FIFO只有一个数字没有填满。如果FIFO的数据在下一个转换之前没有被读出,那么FIFO的数据就会丢失。
Bit6:片内温度读标志位(TEMP_RDY)
当内部温度传感器的数据转换完成,这个中断就会被触发,此时处理器就可以读温度的数据寄存器了。
Bit5:心率数据完成标志位(HR_RDY)
在心率或者SpO2模式下,当每个数据都被搜集后,这个中断就会被触发。一个心率数据只是由一个红外(IR)数据组成。当FIFO数据寄存器被读后,这个中断就会被自动的触发。
Bit4:SpO2数据完成标志位(SPO2_RDY)
在SpO2的模式下,当每次的数据都被转换完成这个中断就会被触发。一个SpO2的数据由一个红外(IR)和一个红光的点组成。当FIFO数据寄存器被读后,这个中断就会被自动的触发。
Bit3、Bit2、Bit1:省略
Bit0:电源准备完成标志位(PWR_RDY)
在开机或者低功耗状态下,当电源电压在过低锁定(UVLO)之下或者之上,控制器(IC)开机并且数据收集完成,这个中断就会被触发。
中断使能寄存器(Interrupt Enable ):
这个寄存器是用来打开相应中断的,和状态寄存器对应,除电源准备完成之外,均可以由软件设置。
FIFO存储器相关寄存器:
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数据寄存器中下一个被发送的数据就是下一个可用的数据。
通常,从I2C接口读取寄存器会自动增加寄存器地址指针,以便在没有I2C重启事件的情况下,可以在突发读取中读取所有寄存器。在MAX30100中,除FIFO_DATA寄存器(0x05)外,所有寄存器都适用。读FIFO_DATA寄存器不会自动递增寄存器地址;突发读取该寄存器一遍又一遍地读取相同的地址。每个样本是4个字节的数据,因此必须读取该寄存器4次才能获得一个样本。另一个例外是0xFF,在0xFF寄存器没有将地址指针推回到0x00之后读取更多字节,并且读取的数据没有意义。
数据FIFO由一个16采样存储区组成,可存储IR和RED ADC数据。由于每个样本由一个IR字和一个RED字组成,因此每个样本有4个字节的数据,因此,总共64个字节的数据可以存储在FIFO中。图2以图形方式显示了FIFO的结构。 FIFO数据左对齐,如表1所示;即,无论ADC分辨率如何,MSB位始终位于第15位。每个数据样本由一个IR和一个红色数据字(2个寄存器)组成,因此读取一个样本需要连续4个I2C字节读取。读取每个4字节样本后,FIFO读指针自动递增。在仅心率模式下,每个样本的第3个和第4个字节返回零,但FIFO的基本结构保持不变。
从FIFO中读取数据的伪代码如下:
第一步:得到FIFO_RD_PTR
开始;
发送器件地址 写 模式
发送 FIFO_RD_PTR(地址0x04);
再次启动开始;
发送器件地址 读 模式
读 FIFO_WR_PTR (地址0x03);
停止;
主控制器检查从FIFO中读取的样本:
NUM_AVAILABLE_SAMPLES = FIFO_WR_PTR – FIFO_RD_PTR(应该考虑到指针包的问题)
NUM_SAMPLES_TO_READ = < NUM_AVAILABLE_SAMPLES
第二步:从FIFO读取 NUM_SAMPLES_TO_READ 的样本:
开始;
发送器件地址 写 模式
发送 FIFO_DATA 地址(0x05);
重新开始;
发送器件地址 写 模式
for( i = 0 ;i< NUM_SAMPLES_TO_READ;i )
{
读 FIFO_DATA;
保存 IR[15:8];
读 FIFO_DATA;
保存 IR[7:0];
读 FIFO_DATA;
保存 R[15:8];
读 FIFO_DATA;
保存 R[15:8];
}
停止;
第三步:写FIFO_RD_PTR寄存器。如果第二步成功了,在FIFO中的FIFO_RD_PTR
指针就会指向下一个样本,那么第三步就不是特别重要了。否则控制器适当的刷新
FIFO_RD_PTR寄存器,因此这个样本就是重新读取。
开始;
发送器件地址 写 模式
发送FIFO_RD_PTR寄存器地址(0x04);
写FIFO_RD_PTR;
停止;
模式配置寄存器(Mode Configuration 0X06):
Bit7:关闭控制(SHDN)
把这一位置为 1,就会设置成省电模式。当是设置成省电模式时,所有的寄存器都会保存他们当前的值,并且其读写功能正常。在这个模式下所有的中断都是被清除。
Bit6:复位控制(RESET)
当这一位被置为1,所有的配置、阀阈值及数据寄存器都会被复位到上电状态。唯一的例外是,当温度数据寄存器0x16和0x17没有被清除时,并且RESET和TEMP_EN都被置1,声明的情况不会发生。当复位完成之后RESET位会被自动置为0.
Bit3:温度使能(TEMP_EN)
当初始化单次温度数据从温度传感器读出来之后,这一位就会被清除。在SPO2或者心率模式下,和上面一样。
Bit2:0:模式控制
这些位设置MAX30100的工作状态。更改模式不会更改任何其他设置,也不会删除数据寄存器中以前存储的任何数据,具体配置如下表。
动脉血氧饱和度配置寄存器(SpO2 Configuration 0x07):
Bit6:高分辨率采样使能SpO2(SPO2_HI_RES_EN)
这位设置为1,SpO2的ADC就会设置成16位采样,LED脉冲为1.6ms(频率625Hz)
Bit5:复位,默认设置为0
Bit4:2 :SpO2采样率设置
这些位被定义为有效扫描率控制,一个扫描由一个IR脉冲/转换和一个RED转换组成。采样率和脉冲宽度是相关的,样本速率在脉冲宽度的时间内设置一个上界。如果用户选择的样本率对于选定的ledpw设置来说太高了,那么可能的最高样本率将被编入寄存器中。
bit1:0:LED脉宽控制
这些位设置了LED脉冲宽度(IR和红色具有相同的脉冲宽度),因此间接地设置了每个样本中ADC的集成时间。ADC的分辨率与集成时间直接相关。
最终效果展示:代码参考的B站大佬的,连接在这:http://github.com/kmakise
,