找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 51271|回复: 113
收起左侧

max30100血氧心率制作stm32源码 OLED显示

  [复制链接]
ID:244405 发表于 2017-10-31 12:09 | 显示全部楼层 |阅读模式
使用模拟iic_MAX30100,简易血氧心率制作,可用正电原子mini板,OLED显示。整套资料提供给大家学习。主函数部分代码
0.png

50Hz每采集一次数据集时间0.02s,共采集800次,用时16s
脉搏每跳动一次对应一个波形的峰值,上图共有20处峰值
计算(20/16)*60=75,可知心跳为每分钟75次

50Hz采集心率数据:
0.png

血氧检测数据处理:
0.png

stm32单片机源程序如下:
  1. #include "stm32f10x.h"
  2. #include "usart.h"
  3. #include "ultrasonic.h"
  4. #include "stm32f10x_gpio.h"
  5. #include "stm32f10x_i2c.h"
  6. #include "delay.h"        //延时函数 1
  7. #include "delayl.h"        //延时函数 2
  8. #include <stdio.h>
  9. #include <math.h>
  10. #include "bsp_i2c_gpio.h"
  11. #include "oled.h"

  12. #define SAMPLE_50   //如果定义此宏就是50采样率   否则是100


  13. /*************************************************
  14. 函数: fputc(int ch, FILE *f)
  15. 功能: 重定向c库函数printf到USART1
  16. 参数: 无
  17. 返回: 无
  18. **************************************************/
  19. int fputc(int ch, FILE *f)
  20. {
  21.         USART_SendData(USART1, (unsigned char) ch);
  22.         while (!(USART1->SR & USART_FLAG_TXE));
  23.         return (ch);
  24. }


  25. #define USR_I2C_USED I2C1

  26. void I2C1_Configuration(void)
  27. {
  28.         I2C_InitTypeDef  I2C_InitStructure;
  29.         GPIO_InitTypeDef  GPIO_InitStructure;

  30.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);
  31.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
  32.    

  33.         /* PB6,7 SCL and SDA */
  34.         GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_6 | GPIO_Pin_7;
  35.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  36.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
  37.         GPIO_Init(GPIOB, &GPIO_InitStructure);
  38.         
  39.     I2C_DeInit(I2C1);
  40.     I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
  41.     I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
  42.     I2C_InitStructure.I2C_OwnAddress1 = 0x30;
  43.     I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
  44.     I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
  45.     I2C_InitStructure.I2C_ClockSpeed = 100000;//100K速度
  46.    
  47.         I2C_Cmd(I2C1, ENABLE);
  48.         I2C_Init(I2C1, &I2C_InitStructure);
  49.         /*允许1字节1应答模式*/
  50.         I2C_AcknowledgeConfig(I2C1, ENABLE);

  51.     printf("I2C1_Configuration----\n\r");
  52. }

  53. void I2C2_Configuration(void)
  54. {
  55.         I2C_InitTypeDef  I2C_InitStructure;
  56.         GPIO_InitTypeDef  GPIO_InitStructure;

  57.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2,ENABLE);
  58.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
  59.    

  60.         /* PB10,11 SCL and SDA */
  61.         GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_10 | GPIO_Pin_11;
  62.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  63.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
  64.         GPIO_Init(GPIOB, &GPIO_InitStructure);
  65.         
  66.     I2C_DeInit(I2C2);
  67.     I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
  68.     I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
  69.     I2C_InitStructure.I2C_OwnAddress1 = 0x30;
  70.     I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
  71.     I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
  72.     I2C_InitStructure.I2C_ClockSpeed = 100000;//100K速度
  73.    
  74.         I2C_Cmd(I2C2, ENABLE);
  75.         I2C_Init(I2C2, &I2C_InitStructure);
  76.         /*允许1字节1应答模式*/
  77.         I2C_AcknowledgeConfig(I2C2, ENABLE);

  78.     printf("I2C2_Configuration----\n\r");
  79. }

  80. void I2C1_GPIO_Config(void)
  81. {

  82.         GPIO_InitTypeDef GPIO_InitStructure; //GPIO结构体定义
  83.         
  84.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//使能I2C的IO口

  85.                 /* 使能与 I2C1 有关的时钟 */
  86.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);
  87.         
  88.         /* PB6-I2C1_SCL、PB7-I2C1_SDA*/
  89.         
  90.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;

  91.         
  92.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  93.         
  94.         //PIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; // 开漏输出
  95.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  96.         
  97.         GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化结构体配置

  98. }

  99. /* I2C 工作模式配置 */

  100. void I2C1_Mode_config(void)
  101. {
  102.                 /*定义I2C结构体*/
  103.         I2C_InitTypeDef  I2C_InitStructure;



  104.                
  105.         /*配置为I2C模式*/
  106.         I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
  107.         
  108.         /*该参数只有在I2C 工作在快速模式(时钟工作频率高于 100KHz)下才有意义。*/
  109.         I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
  110.         
  111.         /*设置第一个设备自身地址*/
  112.         I2C_InitStructure.I2C_OwnAddress1 =0x30;
  113.         
  114.         /*使能应答*/
  115.         I2C_InitStructure.I2C_Ack = I2C_Ack_Enable ;
  116.         
  117.         /*AT24C02地址为7位所以设置7位就行了*/
  118.         I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; /*时钟速率,以HZ为单位的,最高为400khz*/
  119.         
  120.         I2C_InitStructure.I2C_ClockSpeed = 20000;

  121.         /* 使能 I2C1 */
  122.         I2C_Cmd(I2C1, ENABLE);
  123.         
  124.         /* I2C1 初始化 */
  125.         I2C_Init(I2C1, &I2C_InitStructure);
  126.         
  127.         
  128. }

  129. void I2C2_GPIO_Config(void)
  130. {

  131.         GPIO_InitTypeDef GPIO_InitStructure; //GPIO结构体定义
  132.         
  133.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//使能I2C的IO口

  134.                 /* 使能与 I2C1 有关的时钟 */
  135.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2,ENABLE);
  136.         
  137.         /* PB10-I2C2_SCL、PB11-I2C2_SDA*/
  138.         
  139.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;

  140.         
  141.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  142.         
  143.         //PIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; // 开漏输出
  144.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  145.         
  146.         GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化结构体配置

  147. }

  148. /* I2C 工作模式配置 */

  149. void I2C2_Mode_config(void)
  150. {
  151.                 /*定义I2C结构体*/
  152.         I2C_InitTypeDef  I2C_InitStructure;

  153.         /*配置为I2C模式*/
  154.         I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
  155.         
  156.         /*该参数只有在I2C 工作在快速模式(时钟工作频率高于 100KHz)下才有意义。*/
  157.         I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
  158.         
  159.         /*设置第一个设备自身地址*/
  160.         I2C_InitStructure.I2C_OwnAddress1 =0x30;
  161.         
  162.         /*使能应答*/
  163.         I2C_InitStructure.I2C_Ack = I2C_Ack_Enable ;
  164.         
  165.         /*AT24C02地址为7位所以设置7位就行了*/
  166.         I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; /*时钟速率,以HZ为单位的,最高为400khz*/
  167.         
  168.         I2C_InitStructure.I2C_ClockSpeed = 100000;

  169.         /* 使能 I2C1 */
  170.         I2C_Cmd(I2C2, ENABLE);
  171.         
  172.         /* I2C1 初始化 */
  173.         I2C_Init(I2C2, &I2C_InitStructure);
  174.         
  175.         
  176. }


  177. /*************************************************
  178. 函数: void main_init(void)
  179. 功能: main初始化
  180. 参数: 无
  181. 返回: 无
  182. **************************************************/
  183. void main_init(void)
  184. {
  185.         Usart_Init();
  186.         //I2C1_GPIO_Config();
  187.         //I2C1_Mode_config();
  188.         //I2C1_Configuration();
  189.         bsp_InitI2C();
  190.         delay_init(72);            //延时初始化
  191. }

  192. extern void test_max30100_fun(void);
  193. extern u8 max10300_Bus_Read(u8 Register_Address);
  194. extern void max10300_init(void);

  195. /*************************************************
  196. 函数: int main(void)
  197. 功能: main主函数
  198. 参数: 无
  199. 返回: 无
  200. **************************************************/
  201. int main(void)
  202. {
  203.         u8 temp_num=0;
  204.          
  205.         main_init();
  206.         
  207.         max10300_init();
  208.         printf("\r\n stm32 init runing \r\n");
  209.         
  210.         delayl_init();                     //延时函数初始化         
  211.          
  212.         OLED_Init();                        //初始化OLED     

  213.         OLED_ShowString(0,0, "SpO2:",16);  
  214.                   
  215.         OLED_ShowString(0,30,"Heart Rate:",16);  
  216.                   
  217.         OLED_Refresh_Gram();//更新显示到OLED
  218.         
  219.         
  220.         
  221.         
  222.         
  223.         /*
  224. while(1)
  225. {
  226.         delay_ms(1000);            
  227.         max10300_init();
  228.         temp_num = max10300_Bus_Read(0x16);
  229.         printf("当前温度 = %d\r\n",temp_num);
  230. }*/
  231.         while(1)
  232.         {        
  233.                 test_max30100_fun();
  234.                
  235.                
  236.                         
  237.                
  238.                
  239.                
  240.         }
  241. }




  242. #define max10300_WR_address 0xAE

  243. u8 max10300_Bus_Write(u8 Register_Address, u8 Word_Data)
  244. {

  245.         /* 采用串行EEPROM随即读取指令序列,连续读取若干字节 */

  246.         /* 第1步:发起I2C总线启动信号 */
  247.         i2c_Start();

  248.         /* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
  249.         i2c_SendByte(max10300_WR_address | I2C_WR);        /* 此处是写指令 */

  250.         /* 第3步:发送ACK */
  251.         if (i2c_WaitAck() != 0)
  252.         {
  253.                 goto cmd_fail;        /* EEPROM器件无应答 */
  254.         }

  255.         /* 第4步:发送字节地址 */
  256.         i2c_SendByte(Register_Address);
  257.         if (i2c_WaitAck() != 0)
  258.         {
  259.                 goto cmd_fail;        /* EEPROM器件无应答 */
  260.         }
  261.         
  262.         /* 第5步:开始写入数据 */
  263.         i2c_SendByte(Word_Data);

  264.         /* 第6步:发送ACK */
  265.         if (i2c_WaitAck() != 0)
  266.         {
  267.                 goto cmd_fail;        /* EEPROM器件无应答 */
  268.         }

  269.         /* 发送I2C总线停止信号 */
  270.         i2c_Stop();
  271.         return 1;        /* 执行成功 */

  272. cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
  273.         /* 发送I2C总线停止信号 */
  274.         i2c_Stop();
  275.         return 0;
  276. }



  277. u8 max10300_Bus_Read(u8 Register_Address)
  278. {
  279.         u8  data;


  280.         /* 第1步:发起I2C总线启动信号 */
  281.         i2c_Start();

  282.         /* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
  283.         i2c_SendByte(max10300_WR_address | I2C_WR);        /* 此处是写指令 */

  284.         /* 第3步:发送ACK */
  285.         if (i2c_WaitAck() != 0)
  286.         {
  287.                 goto cmd_fail;        /* EEPROM器件无应答 */
  288.         }

  289.         /* 第4步:发送字节地址, */
  290.         i2c_SendByte((uint8_t)Register_Address);
  291.         if (i2c_WaitAck() != 0)
  292.         {
  293.                 goto cmd_fail;        /* EEPROM器件无应答 */
  294.         }
  295.         

  296.         /* 第6步:重新启动I2C总线。下面开始读取数据 */
  297.         i2c_Start();

  298.         /* 第7步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
  299.         i2c_SendByte(max10300_WR_address | I2C_RD);        /* 此处是读指令 */

  300.         /* 第8步:发送ACK */
  301.         if (i2c_WaitAck() != 0)
  302.         {
  303.                 goto cmd_fail;        /* EEPROM器件无应答 */
  304.         }

  305.         /* 第9步:读取数据 */
  306.         {
  307.                 data = i2c_ReadByte();        /* 读1个字节 */

  308.                 i2c_NAck();        /* 最后1个字节读完后,CPU产生NACK信号(驱动SDA = 1) */
  309.         }
  310.         /* 发送I2C总线停止信号 */
  311.         i2c_Stop();
  312.         return data;        /* 执行成功 返回data值 */

  313. cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
  314.         /* 发送I2C总线停止信号 */
  315.         i2c_Stop();
  316.         return 0;
  317. }

  318. static void i2c_Delay(void)
  319. {
  320.         uint8_t i;

  321.         /* 
  322.                 CPU主频168MHz时,在内部Flash运行, MDK工程不优化。用台式示波器观测波形。
  323.                 循环次数为5时,SCL频率 = 1.78MHz (读耗时: 92ms, 读写正常,但是用示波器探头碰上就读写失败。时序接近临界)
  324.                 循环次数为10时,SCL频率 = 1.1MHz (读耗时: 138ms, 读速度: 118724B/s)
  325.                 循环次数为30时,SCL频率 = 440KHz, SCL高电平时间1.0us,SCL低电平时间1.2us

  326.                 上拉电阻选择2.2K欧时,SCL上升沿时间约0.5us,如果选4.7K欧,则上升沿约1us

  327.                 实际应用选择400KHz左右的速率即可
  328.         */
  329.         for (i = 0; i < 30; i++);
  330. }
  331. #if 1
  332. void max10300_FIFO_Read(u8 Register_Address,u16  Word_Data[][2],u8 count)
  333. {
  334.         u8 i=0;
  335.         u8 no = count;
  336.         u8 data1, data2;
  337.         /* 第1步:发起I2C总线启动信号 */
  338.         i2c_Start();

  339.         /* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
  340.         i2c_SendByte(max10300_WR_address | I2C_WR);        /* 此处是写指令 */

  341.         /* 第3步:发送ACK */
  342.         if (i2c_WaitAck() != 0)
  343.         {
  344.                 goto cmd_fail;        /* EEPROM器件无应答 */
  345.         }

  346.         /* 第4步:发送字节地址, */
  347.         i2c_SendByte((uint8_t)Register_Address);
  348.         if (i2c_WaitAck() != 0)
  349.         {
  350.                 goto cmd_fail;        /* EEPROM器件无应答 */
  351.         }
  352.         

  353.         /* 第6步:重新启动I2C总线。下面开始读取数据 */
  354.         i2c_Start();

  355.         /* 第7步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
  356.         i2c_SendByte(max10300_WR_address | I2C_RD);        /* 此处是读指令 */

  357.         /* 第8步:发送ACK */
  358.         if (i2c_WaitAck() != 0)
  359.         {
  360.                 goto cmd_fail;        /* EEPROM器件无应答 */
  361.         }

  362.         /* 第9步:读取数据 */
  363.         while (no)
  364.         {
  365.                 data1 = i2c_ReadByte();        
  366.                 i2c_Ack();
  367.                 data2 = i2c_ReadByte();
  368.                 i2c_Ack();
  369.                 Word_Data[i][0] = (((u16)data1 << 8) | data2);  //

  370.                
  371.                 data1 = i2c_ReadByte();        
  372.                 i2c_Ack();
  373.                 data2 = i2c_ReadByte();
  374.                 if(1==no)
  375.                         i2c_NAck();        /* 最后1个字节读完后,CPU产生NACK信号(驱动SDA = 1) */
  376.                 else
  377.                         i2c_Ack();
  378.                 Word_Data[i][1] = (((u16)data1 << 8) | data2);

  379.                 no--;        
  380.                 i++;
  381.         }
  382.         /* 发送I2C总线停止信号 */
  383.         i2c_Stop();

  384. cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
  385.         /* 发送I2C总线停止信号 */
  386.         i2c_Stop();
  387. }
  388. #else

  389. void max10300_FIFO_Read(u8 Register_Address,u16  Word_Data[][2],u8 count)
  390. {
  391.         u8 i=0;
  392.         u8 no = count;
  393.         u8 data1, data2;
  394.         

  395.         while(I2C_GetFlagStatus(USR_I2C_USED, I2C_FLAG_BUSY))
  396.                 ; //调用库函数检测I2C器件是否处于BUSY状态
  397.                

  398.         I2C_AcknowledgeConfig(USR_I2C_USED, ENABLE);   /*允许1字节1应答模式*/


  399.         I2C_GenerateSTART(USR_I2C_USED, ENABLE);
  400.          while(!I2C_CheckEvent(USR_I2C_USED, I2C_EVENT_MASTER_MODE_SELECT))
  401.                  ; //清除EV5
  402.         
  403.         I2C_Send7bitAddress(USR_I2C_USED, max10300_WR_address, I2C_Direction_Transmitter);
  404.         while(!I2C_CheckEvent(USR_I2C_USED,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
  405.                 ; //ADDR=1,清除EV6
  406.                
  407.         I2C_SendData(USR_I2C_USED, Register_Address);
  408.          while(! I2C_CheckEvent(USR_I2C_USED, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
  409.                  ;//移位寄存器非空,数据寄存器已经空,产生EV8,发送数据到DR既可清除该事件

  410.         I2C_GenerateSTART(USR_I2C_USED, ENABLE);
  411.          while(!I2C_CheckEvent(USR_I2C_USED, I2C_EVENT_MASTER_MODE_SELECT))
  412.                  ; //清除EV5
  413.         
  414.         I2C_Send7bitAddress(USR_I2C_USED, max10300_WR_address, I2C_Direction_Receiver);
  415.         while(!I2C_CheckEvent(USR_I2C_USED, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));


  416.          while (no)
  417.     {        
  418.         
  419.                 while(!I2C_CheckEvent(USR_I2C_USED, I2C_EVENT_MASTER_BYTE_RECEIVED)); // EV7
  420.             data1 = I2C_ReceiveData(USR_I2C_USED);

  421.                 while(!I2C_CheckEvent(USR_I2C_USED, I2C_EVENT_MASTER_BYTE_RECEIVED)); // EV7
  422.             data2 = I2C_ReceiveData(USR_I2C_USED);

  423.             Word_Data[i][0] = (((u16)data1 << 8) | data2);  //



  424.        while(!I2C_CheckEvent(USR_I2C_USED, I2C_EVENT_MASTER_BYTE_RECEIVED)); // EV7
  425.                 data1 = I2C_ReceiveData(USR_I2C_USED);

  426.             if(no==1)
  427.                 {
  428.                      I2C_AcknowledgeConfig(I2C1, DISABLE);        //最后一位后要关闭应答的
  429.                     I2C_GenerateSTOP(I2C1, ENABLE);                        //发送停止位
  430.                
  431.                 }

  432.                 while(!I2C_CheckEvent(USR_I2C_USED, I2C_EVENT_MASTER_BYTE_RECEIVED)); // EV7
  433.                 data2 = I2C_ReceiveData(USR_I2C_USED);

  434.                 Word_Data[i][1] = (((u16)data1 << 8) | data2);
  435.                 i++;

  436.                
  437.             /* Decrement the read bytes counter */
  438.             no--;
  439.     }
  440.         
  441.         I2C_AcknowledgeConfig(USR_I2C_USED, ENABLE);//将应答位使能回去,等待下次通信
  442.         I2C_GenerateSTOP(I2C1, ENABLE);                        //发送停止位

  443. }

  444. #endif
  445. #define INTERRUPT_REG                                          0X00
  446. #define INTERRUPT_REG_A_FULL                          (0X01<<7)
  447. #define INTERRUPT_REG_TEMP_RDY                  (0X01<<6)
  448. #define INTERRUPT_REG_HR_RDY                          (0X01<<5)
  449. #define INTERRUPT_REG_SPO2_RDY                  (0X01<<4)
  450. #define INTERRUPT_REG_PWR_RDY                          (0X01<<0)



  451. void max10300_init()
  452. {
  453.         max10300_Bus_Write(0x06, 0x0b);  //mode configuration : temp_en[3]      MODE[2:0]=010 HR only enabled    011 SP02 enabled
  454.         //max10300_Bus_Write(0x06, 0x0a);  //MODE[2:0]=010 HR only enabled     when used is mode ,the red led is not used.
  455.         max10300_Bus_Write(0x01, 0xF0); //open all of interrupt
  456.         max10300_Bus_Write(INTERRUPT_REG, 0x00); //all interrupt clear
  457.         max10300_Bus_Write(0x09, 0x33); //r_pa=3,ir_pa=3

  458. #ifdef SAMPLE_50
  459.         max10300_Bus_Write(0x07, 0x43); //SPO2_SR[4:2]=000   50 per second    LED_PW[1:0]=11  16BITS
  460. #else
  461.         max10300_Bus_Write(0x07, 0x47); //SPO2_SR[4:2]=001  100 per second    LED_PW[1:0]=11  16BITS
  462. #endif
  463.         
  464.         max10300_Bus_Write(0x02, 0x00);   //set FIFO write Pointer reg = 0x00 for clear it
  465.         max10300_Bus_Write(0x03, 0x00);        //set Over Flow Counter  reg = 0x00 for clear it
  466.         max10300_Bus_Write(0x04, 0x0f);        //set FIFO Read Pointer  reg = 0x0f for   
  467.                                                                                         //waitting  write pointer eq read pointer   to   interrupts  INTERRUPT_REG_A_FULL
  468. }

  469. double my_floor(double x)
  470. {
  471.    double y=x;
  472.     if( (*( ( (int *) &y)+1) & 0x80000000)  != 0) //或者if(x<0)
  473.         return (float)((int)x)-1;
  474.     else
  475.         return (float)((int)x);
  476. }

  477. double my_fmod(double x, double y)
  478. {
  479.    double temp, ret;
  480.   
  481.    if (y == 0.0)
  482.       return 0.0;
  483.    temp = my_floor(x/y);
  484.    ret = x - temp * y;
  485.    if ((x < 0.0) != (y < 0.0))
  486.       ret = ret - y;
  487.    return ret;
  488. }


  489. #define XPI            (3.1415926535897932384626433832795)
  490. #define XENTRY        (100)
  491. #define XINCL        (XPI/2/XENTRY)

  492.   static const double XSinTbl[] = {
  493.         0.00000000000000000  , 0.015707317311820675 , 0.031410759078128292 , 0.047106450709642665 , 0.062790519529313374 ,
  494.         0.078459095727844944 , 0.094108313318514325 , 0.10973431109104528  , 0.12533323356430426  , 0.14090123193758267  ,
  495.         0.15643446504023087  , 0.17192910027940955  , 0.18738131458572463  , 0.20278729535651249  , 0.21814324139654256  ,
  496.         0.23344536385590542  , 0.24868988716485479  , 0.26387304996537292  , 0.27899110603922928  , 0.29404032523230400  ,
  497.         0.30901699437494740  , 0.32391741819814940  , 0.33873792024529142  , 0.35347484377925714  , 0.36812455268467797  ,
  498.         0.38268343236508978  , 0.39714789063478062  , 0.41151435860510882  , 0.42577929156507272  , 0.43993916985591514  ,
  499.         0.45399049973954680  , 0.46792981426057340  , 0.48175367410171532  , 0.49545866843240760  , 0.50904141575037132  ,
  500.         0.52249856471594880  , 0.53582679497899666  , 0.54902281799813180  , 0.56208337785213058  , 0.57500525204327857  ,
  501.         0.58778525229247314  , 0.60042022532588402  , 0.61290705365297649  , 0.62524265633570519  , 0.63742398974868975  ,
  502.         0.64944804833018377  , 0.66131186532365183  , 0.67301251350977331  , 0.68454710592868873  , 0.69591279659231442  ,
  503.         0.70710678118654757  , 0.71812629776318881  , 0.72896862742141155  , 0.73963109497860968  , 0.75011106963045959  ,
  504.         0.76040596560003104  , 0.77051324277578925  , 0.78043040733832969  , 0.79015501237569041  , 0.79968465848709058  ,
  505.         0.80901699437494745  , 0.81814971742502351  , 0.82708057427456183  , 0.83580736136827027  , 0.84432792550201508  ,
  506.         0.85264016435409218  , 0.86074202700394364  , 0.86863151443819120  , 0.87630668004386369  , 0.88376563008869347  ,
  507.         0.89100652418836779  , 0.89802757576061565  , 0.90482705246601958  , 0.91140327663544529  , 0.91775462568398114  ,
  508.         0.92387953251128674  , 0.92977648588825146  , 0.93544403082986738  , 0.94088076895422557  , 0.94608535882754530  ,
  509.         0.95105651629515353  , 0.95579301479833012  , 0.96029368567694307  , 0.96455741845779808  , 0.96858316112863108  ,
  510.         0.97236992039767667  , 0.97591676193874743  , 0.97922281062176575  , 0.98228725072868872  , 0.98510932615477398  ,
  511.         0.98768834059513777  , 0.99002365771655754  , 0.99211470131447788  , 0.99396095545517971  , 0.99556196460308000  ,
  512.         0.99691733373312796  , 0.99802672842827156  , 0.99888987496197001  , 0.99950656036573160  , 0.99987663248166059  ,
  513.         1.00000000000000000  };

  514. double XSin( double x )
  515. {
  516.     int s = 0 , n;
  517.     double dx , sx , cx;
  518.     if( x < 0 )
  519.         s = 1 , x = -x;
  520.     x = my_fmod( x , 2 * XPI );
  521.     if( x > XPI )
  522.         s = !s , x -= XPI;
  523.     if( x > XPI / 2 )
  524.         x = XPI - x;
  525.     n = (int)( x / XINCL );
  526.     dx = x - n * XINCL;
  527.     if( dx > XINCL / 2 )
  528.         ++n , dx -= XINCL;
  529.     sx = XSinTbl[n];
  530.     cx = XSinTbl[XENTRY-n];
  531.     x = sx + dx*cx - (dx*dx)*sx/2
  532.         - (dx*dx*dx)*cx/6
  533.         + (dx*dx*dx*dx)*sx/24
  534.         ;
  535.      
  536.     return s ? -x : x;
  537. }

  538. double XCos( double x )
  539. {
  540.     return XSin( x + XPI/2 );
  541. }


  542. /*********************************FFT*************************************
  543.                          快速傅里叶变换C函数
  544. 函数简介:此函数是通用的快速傅里叶变换C语言函数,移植性强,以下部分不依
  545.           赖硬件。此函数采用联合体的形式表示一个复数,输入为自然顺序的复
  546.           数(输入实数是可令复数虚部为0),输出为经过FFT变换的自然顺序的
  547.           复数
  548. 使用说明:使用此函数只需更改宏定义FFT_N的值即可实现点数的改变,FFT_N的
  549.           应该为2的N次方,不满足此条件时应在后面补0
  550. 函数调用:FFT(s);
  551. 时    间:2010-2-20
  552. 版    本:Ver1.0
  553. 参考文献:     
  554. **********************************************************************/

  555. #define PI 3.1415926535897932384626433832795028841971               //定义圆周率值
  556. #define FFT_N 1024                                                  //定义福利叶变换的点数

  557. struct compx     //定义一个复数结构
  558.         {
  559.                 float real;
  560.                 float imag;
  561.         };                  

  562. struct compx s1[FFT_N+16];           //FFT输入和输出:从S[1]开始存放,根据大小自己定义
  563. struct compx s2[FFT_N+16];           //FFT输入和输出:从S[1]开始存放,根据大小自己定义


  564. /*******************************************************************
  565. 函数原型:struct compx EE(struct compx b1,struct compx b2)  
  566. 函数功能:对两个复数进行乘法运算
  567. 输入参数:两个以联合体定义的复数a,b
  568. 输出参数:a和b的乘积,以联合体的形式输出
  569. *******************************************************************/
  570. struct compx EE(struct compx a,struct compx b)      
  571. {
  572.          struct compx c;
  573.          c.real=a.real*b.real-a.imag*b.imag;
  574.          c.imag=a.real*b.imag+a.imag*b.real;
  575.          return(c);
  576. }

  577. /*****************************************************************
  578. 函数原型:void FFT(struct compx *xin,int N)
  579. 函数功能:对输入的复数组进行快速傅里叶变换(FFT)
  580. 输入参数:*xin复数结构体组的首地址指针,struct型
  581. *****************************************************************/
  582. void FFT(struct compx *xin)
  583. {
  584.         int f,m,nv2,nm1,i,k,l,j=0;
  585.         struct compx u,w,t;

  586.         nv2=FFT_N/2;                  //变址运算,即把自然顺序变成倒位序,采用雷德算法
  587.         nm1=FFT_N-1;  
  588.         for(i=0;i<nm1;i++)        
  589.         {
  590.                 if(i<j)                    //如果i<j,即进行变址
  591.                 {
  592.                         t=xin[j];           
  593.                         xin[j]=xin[i];
  594.                         xin[i]=t;
  595.                 }
  596.                 k=nv2;                    //求j的下一个倒位序
  597.                
  598.                 while(k<=j)               //如果k<=j,表示j的最高位为1   
  599.                 {           
  600.                         j=j-k;                 //把最高位变成0
  601.                         k=k/2;                 //k/2,比较次高位,依次类推,逐个比较,直到某个位为0
  602.                 }
  603.                
  604.                 j=j+k;                   //把0改为1
  605.         }
  606.          
  607.         {  //FFT运算核,使用蝶形运算完成FFT运算
  608.                 int le,lei,ip;                           
  609.                 f=FFT_N;
  610.                 for(l=1;(f=f/2)!=1;l++)                  //计算l的值,即计算蝶形级数
  611.                         ;
  612.                 for(m=1;m<=l;m++)                           // 控制蝶形结级数
  613.                 {                                           //m表示第m级蝶形,l为蝶形级总数l=log(2)N
  614.                         le=2<<(m-1);                            //le蝶形结距离,即第m级蝶形的蝶形结相距le点
  615.                         lei=le/2;                               //同一蝶形结中参加运算的两点的距离
  616.                         u.real=1.0;                             //u为蝶形结运算系数,初始值为1
  617.                         u.imag=0.0;
  618.                         w.real=XCos(PI/lei);                     //w为系数商,即当前系数与前一个系数的商
  619.                         w.imag=-XSin(PI/lei);
  620.                         for(j=0;j<=lei-1;j++)                   //控制计算不同种蝶形结,即计算系数不同的蝶形结
  621.                         {
  622.                                 for(i=j;i<=FFT_N-1;i=i+le)            //控制同一蝶形结运算,即计算系数相同蝶形结
  623.                                 {
  624.                                         ip=i+lei;                           //i,ip分别表示参加蝶形运算的两个节点
  625.                                         t=EE(xin[ip],u);                    //蝶形运算,详见公式
  626.                                         xin[ip].real=xin[i].real-t.real;
  627.                                         xin[ip].imag=xin[i].imag-t.imag;
  628.                                         xin[i].real=xin[i].real+t.real;
  629.                                         xin[i].imag=xin[i].imag+t.imag;
  630.                                 }
  631.                                 u=EE(u,w);                           //改变系数,进行下一个蝶形运算
  632.                         }
  633.                 }
  634.         }

  635. }

  636. u16 g_fft_index=0;

  637. u16 qsqrt(u32 a)
  638. {
  639.   u32 rem = 0, root = 0, divisor = 0;
  640.   u16 i;
  641.   for(i=0; i<16; i++)
  642.   {
  643.     root <<= 1;
  644.     rem = ((rem << 2) + (a>>30));
  645.     a <<= 2;
  646.     divisor = (root << 1) + 1;
  647.     if(divisor <= rem)
  648.     {
  649.       rem -= divisor;
  650.       root++;
  651.     }
  652.   }
  653.   return root;
  654. }

  655. #define START_INDEX    10   //滤出低频干扰
  656. u16 find_max_num_index(struct compx *data,u16 count)
  657. {
  658.         u16 i=START_INDEX;
  659.         u16 max_num_index = i;
  660.         //struct compx temp=data[i];
  661.         float temp = data[i].real;
  662.         for(i=START_INDEX;i<count;i++)
  663.         {
  664.                 if(temp < data[i].real)
  665.                 {
  666.                         temp = data[i].real;
  667.                         max_num_index = i;
  668.                 }
  669.         }
  670.         printf("max_num_index=%d\r\n",max_num_index);
  671.         return max_num_index;
  672.         
  673. }

  674. #define CORRECTED_VALUE        50   //粗略标定血液氧气含量   ,精准数据需要大量测量

  675. void sp02_treated_fun(u16 max_index)
  676. {
  677.         float sp02_num=0;
  678.          
  679.         delayl_init();                     //延时函数初始化           
  680.         OLED_Init();                        //初始化OLED     
  681.    
  682.          printf("\r\n zhiliu s1=%f,s2=%f \r\n",s1[0].real,s2[0].real);
  683.         printf("\r\n s1=%f,s2=%f \r\n",s1[max_index].real,s2[max_index].real);
  684.         if((s1[max_index].real*s2[0].real)>(s2[max_index].real*s1[0].real))  //if   ir>red      sp02>75%
  685.         {
  686.                 sp02_num = (s2[max_index].real*s1[0].real)/(s1[max_index].real*s2[0].real);
  687.                 printf("\r\nsp02_num  : %f\r\n",sp02_num*100);
  688.                 printf("\r\n血氧含量为: %f\r\n",(1-sp02_num)*100+CORRECTED_VALUE);
  689.                
  690.                 OLED_ShowString(0,0, "SpO2:",16);
  691.                 if((1-sp02_num)*100+CORRECTED_VALUE>99)
  692.                         OLED_ShowString(40,0, "99",16);
  693.                 else
  694.                         OLED_ShowNum(40,0,(1-sp02_num)*100+CORRECTED_VALUE,4,16);

  695.                 OLED_ShowString(80,0,"%",16);
  696.                 OLED_ShowString(0,30,"Heart Rate:",12);   
  697.                 OLED_Refresh_Gram();//更新显示到OLED         
  698.                
  699.         }
  700.         else   // sp02<75%
  701.         {
  702.                 printf("\r\n 严重缺氧! \r\n");
  703.                
  704.                 OLED_ShowString(0,0, "SpO2:",16);
  705.                 OLED_ShowString(40,0,"ANOXIA!",16);
  706.                 OLED_ShowString(0,30,"Heart Rate:",12);   
  707.                 OLED_Refresh_Gram();//更新显示到OLED
  708.         }        
  709. }

  710. void test_max30100_fun(void)
  711. {
  712.         u16 temp_num=0;
  713.         u16 fifo_word_buff[15][2];
  714.         u16 Heart_Rate=0;
  715.         u16 s1_max_index=0;
  716.         u16 s2_max_index=0;
  717.         
  718. ……………………

  719. …………限于本文篇幅 余下代码请从51黑下载附件…………
复制代码
0.png
所有资料51hei提供下载:
血氧心率.7z (4.33 MB, 下载次数: 1349)

评分

参与人数 7黑币 +52 收起 理由
wait_wait_ + 12 很给力!
mettie + 5 很给力!
lqzhappy + 5 很给力!
kat5566 + 5
兰木沧溟 + 5 很给力!
fhx97 + 15 很给力!
hei1043218814 + 5 很给力!

查看全部评分

回复

使用道具 举报

ID:246166 发表于 2017-11-8 18:39 | 显示全部楼层
楼主太强了,学习学习
回复

使用道具 举报

ID:249501 发表于 2017-11-14 22:52 | 显示全部楼层
不错啊  学习一个
回复

使用道具 举报

ID:249504 发表于 2017-11-15 10:45 | 显示全部楼层
楼主,你有两个I2C函数,都用了 PB6和PB7引脚,一个是复用开漏,一个是复用推挽,请问为什么这样写呢?谢谢
回复

使用道具 举报

ID:245513 发表于 2017-11-18 16:48 来自手机 | 显示全部楼层
伊森一亨特 发表于 2017-11-15 10:45
楼主,你有两个I2C函数,都用了 PB6和PB7引脚,一个是复用开漏,一个是复用推挽,请问为什么这样写呢?谢谢

模拟iic的。应该是sclk和sda。肯定要用不同gpio配置方式。自己看看iic协议吧。
回复

使用道具 举报

ID:252080 发表于 2017-11-22 16:17 | 显示全部楼层
学习学习
回复

使用道具 举报

ID:254920 发表于 2017-11-28 21:36 | 显示全部楼层
厉害厉害
回复

使用道具 举报

ID:249501 发表于 2017-12-21 10:02 | 显示全部楼层
请问读FIFO是怎么读?读到中断标志位就一直读吗?还是读到中断就只读4个样本?
回复

使用道具 举报

ID:180844 发表于 2018-1-15 16:37 | 显示全部楼层
请问数据处理方面的算法在哪部分
回复

使用道具 举报

ID:284715 发表于 2018-2-22 10:56 | 显示全部楼层
学习了~
回复

使用道具 举报

ID:284715 发表于 2018-2-22 14:07 | 显示全部楼层
求教楼主,为什么我采集到的数据是这样的,数据值一直在变小啊
1519279446(1).png
回复

使用道具 举报

ID:288809 发表于 2018-3-8 10:05 | 显示全部楼层
想学习一下,分不够了
回复

使用道具 举报

ID:292418 发表于 2018-3-15 18:18 来自手机 | 显示全部楼层
楼主流弊,学习学习
回复

使用道具 举报

ID:239244 发表于 2018-3-19 21:12 | 显示全部楼层
helloapy 发表于 2018-2-22 14:07
求教楼主,为什么我采集到的数据是这样的,数据值一直在变小啊

兄弟,咱俩遇到的问题一样啊,不知道你解决了没有
回复

使用道具 举报

ID:296605 发表于 2018-3-25 09:23 | 显示全部楼层
你好,请问你所用的iic-max30100模块可以发我一下淘宝链接吗?找不到带LCD显示屏的30100模块,麻烦了。
回复

使用道具 举报

ID:289262 发表于 2018-3-29 09:44 | 显示全部楼层
好东西,学习下。
回复

使用道具 举报

ID:299299 发表于 2018-4-3 23:36 | 显示全部楼层
请问楼主这个算法有一些资料文档吗,可以分享一下吗,谢谢
回复

使用道具 举报

ID:299299 发表于 2018-4-5 09:49 | 显示全部楼层
请问楼主这个可以用来放在手腕上测量吗,我调试了你的代码发现在手指测量可以得出想要得到的波形,但手腕上却不太一样
回复

使用道具 举报

ID:273296 发表于 2018-4-13 11:03 | 显示全部楼层
学习了
回复

使用道具 举报

ID:198849 发表于 2018-4-13 15:53 | 显示全部楼层
楼主,我参考你的移植到F4上,接上线没反应怎么回事?灯都不亮
回复

使用道具 举报

ID:198849 发表于 2018-4-14 09:54 | 显示全部楼层
我一直测出来心率为29
回复

使用道具 举报

ID:314754 发表于 2018-4-23 19:39 | 显示全部楼层
学习了
回复

使用道具 举报

ID:314857 发表于 2018-4-23 21:38 | 显示全部楼层
楼主厉害
回复

使用道具 举报

ID:315693 发表于 2018-4-24 20:45 | 显示全部楼层

楼主太给力啦
回复

使用道具 举报

ID:317239 发表于 2018-4-27 11:36 | 显示全部楼层
附件挂了,楼主能在传一份吗
回复

使用道具 举报

ID:319881 发表于 2018-5-1 15:24 | 显示全部楼层
楼主很强
回复

使用道具 举报

ID:319881 发表于 2018-5-1 15:29 | 显示全部楼层
楼主很强
回复

使用道具 举报

ID:195609 发表于 2018-5-2 10:22 | 显示全部楼层
请问楼主用的MAX30100长什么样子 有链接吗
回复

使用道具 举报

ID:313051 发表于 2018-5-2 19:26 | 显示全部楼层
学习了
回复

使用道具 举报

ID:321333 发表于 2018-5-3 16:18 | 显示全部楼层
厉害厉害 学习学习
回复

使用道具 举报

ID:321333 发表于 2018-5-3 16:56 | 显示全部楼层
楼主用的是什么采集软件
回复

使用道具 举报

ID:280272 发表于 2018-5-3 21:29 | 显示全部楼层
厉害厉害 学习学习
回复

使用道具 举报

ID:322057 发表于 2018-5-4 15:28 | 显示全部楼层
资料下载了不能用,是什么原因?
回复

使用道具 举报

ID:322057 发表于 2018-5-4 15:29 | 显示全部楼层
nandyku 发表于 2018-4-13 15:53
楼主,我参考你的移植到F4上,接上线没反应怎么回事?灯都不亮

请问,你的问题解决了吗?
回复

使用道具 举报

ID:322057 发表于 2018-5-4 16:49 | 显示全部楼层
有可以用的代码吗?可以发一下吗?
回复

使用道具 举报

ID:334304 发表于 2018-5-20 16:56 | 显示全部楼层
分不够下载啊
回复

使用道具 举报

ID:337583 发表于 2018-5-25 11:13 | 显示全部楼层
楼主,为什么我下载不了这个压缩包?按下载显示服务器错误?
回复

使用道具 举报

ID:117866 发表于 2018-6-3 23:58 | 显示全部楼层
谢谢楼主!
回复

使用道具 举报

ID:344516 发表于 2018-6-4 11:17 | 显示全部楼层
感谢分享
回复

使用道具 举报

ID:345398 发表于 2018-6-5 14:30 | 显示全部楼层
谢谢楼主分享,楼主制作和上传辛苦了
回复

使用道具 举报

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

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表