使用模擬iic_MAX30100,簡易血氧心率制作,可用正電原子mini板,OLED顯示。整套資料提供給大家學習。 主函數部分代碼
0.png (448.49 KB, 下載次數: 203)
下載附件
2017-10-31 17:05 上傳
50Hz每采集一次數據集時間0.02s,共采集800次,用時16s
脈搏每跳動一次對應一個波形的峰值,上圖共有20處峰值
計算(20/16)*60=75,可知心跳為每分鐘75次
50Hz采集心率數據:
0.png (93.02 KB, 下載次數: 148)
下載附件
2017-10-31 17:07 上傳
血氧檢測數據處理:
0.png (19.32 KB, 下載次數: 128)
下載附件
2017-10-31 17:07 上傳
stm32單片機源程序如下:
- #include "stm32f10x.h"
- #include "usart.h"
- #include "ultrasonic.h"
- #include "stm32f10x_gpio.h"
- #include "stm32f10x_i2c.h"
- #include "delay.h" //延時函數 1
- #include "delayl.h" //延時函數 2
- #include <stdio.h>
- #include <math.h>
- #include "bsp_i2c_gpio.h"
- #include "oled.h"
- #define SAMPLE_50 //如果定義此宏就是50采樣率 否則是100
-
- /*************************************************
- 函數: fputc(int ch, FILE *f)
- 功能: 重定向c庫函數printf到USART1
- 參數: 無
- 返回: 無
- **************************************************/
- int fputc(int ch, FILE *f)
- {
- USART_SendData(USART1, (unsigned char) ch);
- while (!(USART1->SR & USART_FLAG_TXE));
- return (ch);
- }
- #define USR_I2C_USED I2C1
- void I2C1_Configuration(void)
- {
- I2C_InitTypeDef I2C_InitStructure;
- GPIO_InitTypeDef GPIO_InitStructure;
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
-
- /* PB6,7 SCL and SDA */
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
- GPIO_Init(GPIOB, &GPIO_InitStructure);
-
- I2C_DeInit(I2C1);
- I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
- I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
- I2C_InitStructure.I2C_OwnAddress1 = 0x30;
- I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
- I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
- I2C_InitStructure.I2C_ClockSpeed = 100000;//100K速度
-
- I2C_Cmd(I2C1, ENABLE);
- I2C_Init(I2C1, &I2C_InitStructure);
- /*允許1字節1應答模式*/
- I2C_AcknowledgeConfig(I2C1, ENABLE);
- printf("I2C1_Configuration----\n\r");
- }
- void I2C2_Configuration(void)
- {
- I2C_InitTypeDef I2C_InitStructure;
- GPIO_InitTypeDef GPIO_InitStructure;
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2,ENABLE);
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
-
- /* PB10,11 SCL and SDA */
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
- GPIO_Init(GPIOB, &GPIO_InitStructure);
-
- I2C_DeInit(I2C2);
- I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
- I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
- I2C_InitStructure.I2C_OwnAddress1 = 0x30;
- I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
- I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
- I2C_InitStructure.I2C_ClockSpeed = 100000;//100K速度
-
- I2C_Cmd(I2C2, ENABLE);
- I2C_Init(I2C2, &I2C_InitStructure);
- /*允許1字節1應答模式*/
- I2C_AcknowledgeConfig(I2C2, ENABLE);
- printf("I2C2_Configuration----\n\r");
- }
- void I2C1_GPIO_Config(void)
- {
- GPIO_InitTypeDef GPIO_InitStructure; //GPIO結構體定義
-
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//使能I2C的IO口
- /* 使能與 I2C1 有關的時鐘 */
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);
-
- /* PB6-I2C1_SCL、PB7-I2C1_SDA*/
-
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
-
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
-
- //PIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; // 開漏輸出
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
-
- GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化結構體配置
- }
- /* I2C 工作模式配置 */
- void I2C1_Mode_config(void)
- {
- /*定義I2C結構體*/
- I2C_InitTypeDef I2C_InitStructure;
-
- /*配置為I2C模式*/
- I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
-
- /*該參數只有在I2C 工作在快速模式(時鐘工作頻率高于 100KHz)下才有意義。*/
- I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
-
- /*設置第一個設備自身地址*/
- I2C_InitStructure.I2C_OwnAddress1 =0x30;
-
- /*使能應答*/
- I2C_InitStructure.I2C_Ack = I2C_Ack_Enable ;
-
- /*AT24C02地址為7位所以設置7位就行了*/
- I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; /*時鐘速率,以HZ為單位的,最高為400khz*/
-
- I2C_InitStructure.I2C_ClockSpeed = 20000;
- /* 使能 I2C1 */
- I2C_Cmd(I2C1, ENABLE);
-
- /* I2C1 初始化 */
- I2C_Init(I2C1, &I2C_InitStructure);
-
-
- }
- void I2C2_GPIO_Config(void)
- {
- GPIO_InitTypeDef GPIO_InitStructure; //GPIO結構體定義
-
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//使能I2C的IO口
- /* 使能與 I2C1 有關的時鐘 */
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2,ENABLE);
-
- /* PB10-I2C2_SCL、PB11-I2C2_SDA*/
-
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
-
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
-
- //PIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; // 開漏輸出
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
-
- GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化結構體配置
- }
- /* I2C 工作模式配置 */
- void I2C2_Mode_config(void)
- {
- /*定義I2C結構體*/
- I2C_InitTypeDef I2C_InitStructure;
- /*配置為I2C模式*/
- I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
-
- /*該參數只有在I2C 工作在快速模式(時鐘工作頻率高于 100KHz)下才有意義。*/
- I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
-
- /*設置第一個設備自身地址*/
- I2C_InitStructure.I2C_OwnAddress1 =0x30;
-
- /*使能應答*/
- I2C_InitStructure.I2C_Ack = I2C_Ack_Enable ;
-
- /*AT24C02地址為7位所以設置7位就行了*/
- I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; /*時鐘速率,以HZ為單位的,最高為400khz*/
-
- I2C_InitStructure.I2C_ClockSpeed = 100000;
- /* 使能 I2C1 */
- I2C_Cmd(I2C2, ENABLE);
-
- /* I2C1 初始化 */
- I2C_Init(I2C2, &I2C_InitStructure);
-
-
- }
- /*************************************************
- 函數: void main_init(void)
- 功能: main初始化
- 參數: 無
- 返回: 無
- **************************************************/
- void main_init(void)
- {
- Usart_Init();
- //I2C1_GPIO_Config();
- //I2C1_Mode_config();
- //I2C1_Configuration();
- bsp_InitI2C();
- delay_init(72); //延時初始化
- }
- extern void test_max30100_fun(void);
- extern u8 max10300_Bus_Read(u8 Register_Address);
- extern void max10300_init(void);
- /*************************************************
- 函數: int main(void)
- 功能: main主函數
- 參數: 無
- 返回: 無
- **************************************************/
- int main(void)
- {
- u8 temp_num=0;
-
- main_init();
-
- max10300_init();
- printf("\r\n stm32 init runing \r\n");
-
- delayl_init(); //延時函數初始化
-
- OLED_Init(); //初始化OLED
-
- OLED_ShowString(0,0, "SpO2:",16);
-
- OLED_ShowString(0,30,"Heart Rate:",16);
-
- OLED_Refresh_Gram();//更新顯示到OLED
-
-
-
-
-
- /*
- while(1)
- {
- delay_ms(1000);
- max10300_init();
- temp_num = max10300_Bus_Read(0x16);
- printf("當前溫度 = %d\r\n",temp_num);
- }*/
- while(1)
- {
- test_max30100_fun();
-
-
-
-
-
-
- }
- }
- #define max10300_WR_address 0xAE
- u8 max10300_Bus_Write(u8 Register_Address, u8 Word_Data)
- {
- /* 采用串行EEPROM隨即讀取指令序列,連續讀取若干字節 */
- /* 第1步:發起I2C總線啟動信號 */
- i2c_Start();
- /* 第2步:發起控制字節,高7bit是地址,bit0是讀寫控制位,0表示寫,1表示讀 */
- i2c_SendByte(max10300_WR_address | I2C_WR); /* 此處是寫指令 */
- /* 第3步:發送ACK */
- if (i2c_WaitAck() != 0)
- {
- goto cmd_fail; /* EEPROM器件無應答 */
- }
- /* 第4步:發送字節地址 */
- i2c_SendByte(Register_Address);
- if (i2c_WaitAck() != 0)
- {
- goto cmd_fail; /* EEPROM器件無應答 */
- }
-
- /* 第5步:開始寫入數據 */
- i2c_SendByte(Word_Data);
- /* 第6步:發送ACK */
- if (i2c_WaitAck() != 0)
- {
- goto cmd_fail; /* EEPROM器件無應答 */
- }
- /* 發送I2C總線停止信號 */
- i2c_Stop();
- return 1; /* 執行成功 */
- cmd_fail: /* 命令執行失敗后,切記發送停止信號,避免影響I2C總線上其他設備 */
- /* 發送I2C總線停止信號 */
- i2c_Stop();
- return 0;
- }
- u8 max10300_Bus_Read(u8 Register_Address)
- {
- u8 data;
- /* 第1步:發起I2C總線啟動信號 */
- i2c_Start();
- /* 第2步:發起控制字節,高7bit是地址,bit0是讀寫控制位,0表示寫,1表示讀 */
- i2c_SendByte(max10300_WR_address | I2C_WR); /* 此處是寫指令 */
- /* 第3步:發送ACK */
- if (i2c_WaitAck() != 0)
- {
- goto cmd_fail; /* EEPROM器件無應答 */
- }
- /* 第4步:發送字節地址, */
- i2c_SendByte((uint8_t)Register_Address);
- if (i2c_WaitAck() != 0)
- {
- goto cmd_fail; /* EEPROM器件無應答 */
- }
-
- /* 第6步:重新啟動I2C總線。下面開始讀取數據 */
- i2c_Start();
- /* 第7步:發起控制字節,高7bit是地址,bit0是讀寫控制位,0表示寫,1表示讀 */
- i2c_SendByte(max10300_WR_address | I2C_RD); /* 此處是讀指令 */
- /* 第8步:發送ACK */
- if (i2c_WaitAck() != 0)
- {
- goto cmd_fail; /* EEPROM器件無應答 */
- }
- /* 第9步:讀取數據 */
- {
- data = i2c_ReadByte(); /* 讀1個字節 */
- i2c_NAck(); /* 最后1個字節讀完后,CPU產生NACK信號(驅動SDA = 1) */
- }
- /* 發送I2C總線停止信號 */
- i2c_Stop();
- return data; /* 執行成功 返回data值 */
- cmd_fail: /* 命令執行失敗后,切記發送停止信號,避免影響I2C總線上其他設備 */
- /* 發送I2C總線停止信號 */
- i2c_Stop();
- return 0;
- }
- static void i2c_Delay(void)
- {
- uint8_t i;
- /*
- CPU主頻168MHz時,在內部Flash運行, MDK工程不優化。用臺式示波器觀測波形。
- 循環次數為5時,SCL頻率 = 1.78MHz (讀耗時: 92ms, 讀寫正常,但是用示波器探頭碰上就讀寫失敗。時序接近臨界)
- 循環次數為10時,SCL頻率 = 1.1MHz (讀耗時: 138ms, 讀速度: 118724B/s)
- 循環次數為30時,SCL頻率 = 440KHz, SCL高電平時間1.0us,SCL低電平時間1.2us
- 上拉電阻選擇2.2K歐時,SCL上升沿時間約0.5us,如果選4.7K歐,則上升沿約1us
- 實際應用選擇400KHz左右的速率即可
- */
- for (i = 0; i < 30; i++);
- }
- #if 1
- void max10300_FIFO_Read(u8 Register_Address,u16 Word_Data[][2],u8 count)
- {
- u8 i=0;
- u8 no = count;
- u8 data1, data2;
- /* 第1步:發起I2C總線啟動信號 */
- i2c_Start();
- /* 第2步:發起控制字節,高7bit是地址,bit0是讀寫控制位,0表示寫,1表示讀 */
- i2c_SendByte(max10300_WR_address | I2C_WR); /* 此處是寫指令 */
- /* 第3步:發送ACK */
- if (i2c_WaitAck() != 0)
- {
- goto cmd_fail; /* EEPROM器件無應答 */
- }
- /* 第4步:發送字節地址, */
- i2c_SendByte((uint8_t)Register_Address);
- if (i2c_WaitAck() != 0)
- {
- goto cmd_fail; /* EEPROM器件無應答 */
- }
-
- /* 第6步:重新啟動I2C總線。下面開始讀取數據 */
- i2c_Start();
- /* 第7步:發起控制字節,高7bit是地址,bit0是讀寫控制位,0表示寫,1表示讀 */
- i2c_SendByte(max10300_WR_address | I2C_RD); /* 此處是讀指令 */
- /* 第8步:發送ACK */
- if (i2c_WaitAck() != 0)
- {
- goto cmd_fail; /* EEPROM器件無應答 */
- }
- /* 第9步:讀取數據 */
- while (no)
- {
- data1 = i2c_ReadByte();
- i2c_Ack();
- data2 = i2c_ReadByte();
- i2c_Ack();
- Word_Data[i][0] = (((u16)data1 << 8) | data2); //
-
- data1 = i2c_ReadByte();
- i2c_Ack();
- data2 = i2c_ReadByte();
- if(1==no)
- i2c_NAck(); /* 最后1個字節讀完后,CPU產生NACK信號(驅動SDA = 1) */
- else
- i2c_Ack();
- Word_Data[i][1] = (((u16)data1 << 8) | data2);
- no--;
- i++;
- }
- /* 發送I2C總線停止信號 */
- i2c_Stop();
- cmd_fail: /* 命令執行失敗后,切記發送停止信號,避免影響I2C總線上其他設備 */
- /* 發送I2C總線停止信號 */
- i2c_Stop();
- }
- #else
- void max10300_FIFO_Read(u8 Register_Address,u16 Word_Data[][2],u8 count)
- {
- u8 i=0;
- u8 no = count;
- u8 data1, data2;
-
- while(I2C_GetFlagStatus(USR_I2C_USED, I2C_FLAG_BUSY))
- ; //調用庫函數檢測I2C器件是否處于BUSY狀態
-
- I2C_AcknowledgeConfig(USR_I2C_USED, ENABLE); /*允許1字節1應答模式*/
- I2C_GenerateSTART(USR_I2C_USED, ENABLE);
- while(!I2C_CheckEvent(USR_I2C_USED, I2C_EVENT_MASTER_MODE_SELECT))
- ; //清除EV5
-
- I2C_Send7bitAddress(USR_I2C_USED, max10300_WR_address, I2C_Direction_Transmitter);
- while(!I2C_CheckEvent(USR_I2C_USED,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
- ; //ADDR=1,清除EV6
-
- I2C_SendData(USR_I2C_USED, Register_Address);
- while(! I2C_CheckEvent(USR_I2C_USED, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
- ;//移位寄存器非空,數據寄存器已經空,產生EV8,發送數據到DR既可清除該事件
- I2C_GenerateSTART(USR_I2C_USED, ENABLE);
- while(!I2C_CheckEvent(USR_I2C_USED, I2C_EVENT_MASTER_MODE_SELECT))
- ; //清除EV5
-
- I2C_Send7bitAddress(USR_I2C_USED, max10300_WR_address, I2C_Direction_Receiver);
- while(!I2C_CheckEvent(USR_I2C_USED, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));
- while (no)
- {
-
- while(!I2C_CheckEvent(USR_I2C_USED, I2C_EVENT_MASTER_BYTE_RECEIVED)); // EV7
- data1 = I2C_ReceiveData(USR_I2C_USED);
- while(!I2C_CheckEvent(USR_I2C_USED, I2C_EVENT_MASTER_BYTE_RECEIVED)); // EV7
- data2 = I2C_ReceiveData(USR_I2C_USED);
- Word_Data[i][0] = (((u16)data1 << 8) | data2); //
- while(!I2C_CheckEvent(USR_I2C_USED, I2C_EVENT_MASTER_BYTE_RECEIVED)); // EV7
- data1 = I2C_ReceiveData(USR_I2C_USED);
- if(no==1)
- {
- I2C_AcknowledgeConfig(I2C1, DISABLE); //最后一位后要關閉應答的
- I2C_GenerateSTOP(I2C1, ENABLE); //發送停止位
-
- }
- while(!I2C_CheckEvent(USR_I2C_USED, I2C_EVENT_MASTER_BYTE_RECEIVED)); // EV7
- data2 = I2C_ReceiveData(USR_I2C_USED);
- Word_Data[i][1] = (((u16)data1 << 8) | data2);
- i++;
-
- /* Decrement the read bytes counter */
- no--;
- }
-
- I2C_AcknowledgeConfig(USR_I2C_USED, ENABLE);//將應答位使能回去,等待下次通信
- I2C_GenerateSTOP(I2C1, ENABLE); //發送停止位
- }
- #endif
- #define INTERRUPT_REG 0X00
- #define INTERRUPT_REG_A_FULL (0X01<<7)
- #define INTERRUPT_REG_TEMP_RDY (0X01<<6)
- #define INTERRUPT_REG_HR_RDY (0X01<<5)
- #define INTERRUPT_REG_SPO2_RDY (0X01<<4)
- #define INTERRUPT_REG_PWR_RDY (0X01<<0)
- void max10300_init()
- {
- max10300_Bus_Write(0x06, 0x0b); //mode configuration : temp_en[3] MODE[2:0]=010 HR only enabled 011 SP02 enabled
- //max10300_Bus_Write(0x06, 0x0a); //MODE[2:0]=010 HR only enabled when used is mode ,the red led is not used.
- max10300_Bus_Write(0x01, 0xF0); //open all of interrupt
- max10300_Bus_Write(INTERRUPT_REG, 0x00); //all interrupt clear
- max10300_Bus_Write(0x09, 0x33); //r_pa=3,ir_pa=3
- #ifdef SAMPLE_50
- max10300_Bus_Write(0x07, 0x43); //SPO2_SR[4:2]=000 50 per second LED_PW[1:0]=11 16BITS
- #else
- max10300_Bus_Write(0x07, 0x47); //SPO2_SR[4:2]=001 100 per second LED_PW[1:0]=11 16BITS
- #endif
-
- max10300_Bus_Write(0x02, 0x00); //set FIFO write Pointer reg = 0x00 for clear it
- max10300_Bus_Write(0x03, 0x00); //set Over Flow Counter reg = 0x00 for clear it
- max10300_Bus_Write(0x04, 0x0f); //set FIFO Read Pointer reg = 0x0f for
- //waitting write pointer eq read pointer to interrupts INTERRUPT_REG_A_FULL
- }
- double my_floor(double x)
- {
- double y=x;
- if( (*( ( (int *) &y)+1) & 0x80000000) != 0) //或者if(x<0)
- return (float)((int)x)-1;
- else
- return (float)((int)x);
- }
- double my_fmod(double x, double y)
- {
- double temp, ret;
-
- if (y == 0.0)
- return 0.0;
- temp = my_floor(x/y);
- ret = x - temp * y;
- if ((x < 0.0) != (y < 0.0))
- ret = ret - y;
- return ret;
- }
- #define XPI (3.1415926535897932384626433832795)
- #define XENTRY (100)
- #define XINCL (XPI/2/XENTRY)
- static const double XSinTbl[] = {
- 0.00000000000000000 , 0.015707317311820675 , 0.031410759078128292 , 0.047106450709642665 , 0.062790519529313374 ,
- 0.078459095727844944 , 0.094108313318514325 , 0.10973431109104528 , 0.12533323356430426 , 0.14090123193758267 ,
- 0.15643446504023087 , 0.17192910027940955 , 0.18738131458572463 , 0.20278729535651249 , 0.21814324139654256 ,
- 0.23344536385590542 , 0.24868988716485479 , 0.26387304996537292 , 0.27899110603922928 , 0.29404032523230400 ,
- 0.30901699437494740 , 0.32391741819814940 , 0.33873792024529142 , 0.35347484377925714 , 0.36812455268467797 ,
- 0.38268343236508978 , 0.39714789063478062 , 0.41151435860510882 , 0.42577929156507272 , 0.43993916985591514 ,
- 0.45399049973954680 , 0.46792981426057340 , 0.48175367410171532 , 0.49545866843240760 , 0.50904141575037132 ,
- 0.52249856471594880 , 0.53582679497899666 , 0.54902281799813180 , 0.56208337785213058 , 0.57500525204327857 ,
- 0.58778525229247314 , 0.60042022532588402 , 0.61290705365297649 , 0.62524265633570519 , 0.63742398974868975 ,
- 0.64944804833018377 , 0.66131186532365183 , 0.67301251350977331 , 0.68454710592868873 , 0.69591279659231442 ,
- 0.70710678118654757 , 0.71812629776318881 , 0.72896862742141155 , 0.73963109497860968 , 0.75011106963045959 ,
- 0.76040596560003104 , 0.77051324277578925 , 0.78043040733832969 , 0.79015501237569041 , 0.79968465848709058 ,
- 0.80901699437494745 , 0.81814971742502351 , 0.82708057427456183 , 0.83580736136827027 , 0.84432792550201508 ,
- 0.85264016435409218 , 0.86074202700394364 , 0.86863151443819120 , 0.87630668004386369 , 0.88376563008869347 ,
- 0.89100652418836779 , 0.89802757576061565 , 0.90482705246601958 , 0.91140327663544529 , 0.91775462568398114 ,
- 0.92387953251128674 , 0.92977648588825146 , 0.93544403082986738 , 0.94088076895422557 , 0.94608535882754530 ,
- 0.95105651629515353 , 0.95579301479833012 , 0.96029368567694307 , 0.96455741845779808 , 0.96858316112863108 ,
- 0.97236992039767667 , 0.97591676193874743 , 0.97922281062176575 , 0.98228725072868872 , 0.98510932615477398 ,
- 0.98768834059513777 , 0.99002365771655754 , 0.99211470131447788 , 0.99396095545517971 , 0.99556196460308000 ,
- 0.99691733373312796 , 0.99802672842827156 , 0.99888987496197001 , 0.99950656036573160 , 0.99987663248166059 ,
- 1.00000000000000000 };
- double XSin( double x )
- {
- int s = 0 , n;
- double dx , sx , cx;
- if( x < 0 )
- s = 1 , x = -x;
- x = my_fmod( x , 2 * XPI );
- if( x > XPI )
- s = !s , x -= XPI;
- if( x > XPI / 2 )
- x = XPI - x;
- n = (int)( x / XINCL );
- dx = x - n * XINCL;
- if( dx > XINCL / 2 )
- ++n , dx -= XINCL;
- sx = XSinTbl[n];
- cx = XSinTbl[XENTRY-n];
- x = sx + dx*cx - (dx*dx)*sx/2
- - (dx*dx*dx)*cx/6
- + (dx*dx*dx*dx)*sx/24
- ;
-
- return s ? -x : x;
- }
-
- double XCos( double x )
- {
- return XSin( x + XPI/2 );
- }
- /*********************************FFT*************************************
- 快速傅里葉變換C函數
- 函數簡介:此函數是通用的快速傅里葉變換C語言函數,移植性強,以下部分不依
- 賴硬件。此函數采用聯合體的形式表示一個復數,輸入為自然順序的復
- 數(輸入實數是可令復數虛部為0),輸出為經過FFT變換的自然順序的
- 復數
- 使用說明:使用此函數只需更改宏定義FFT_N的值即可實現點數的改變,FFT_N的
- 應該為2的N次方,不滿足此條件時應在后面補0
- 函數調用:FFT(s);
- 時 間:2010-2-20
- 版 本:Ver1.0
- 參考文獻:
- **********************************************************************/
- #define PI 3.1415926535897932384626433832795028841971 //定義圓周率值
- #define FFT_N 1024 //定義福利葉變換的點數
- struct compx //定義一個復數結構
- {
- float real;
- float imag;
- };
- struct compx s1[FFT_N+16]; //FFT輸入和輸出:從S[1]開始存放,根據大小自己定義
- struct compx s2[FFT_N+16]; //FFT輸入和輸出:從S[1]開始存放,根據大小自己定義
- /*******************************************************************
- 函數原型:struct compx EE(struct compx b1,struct compx b2)
- 函數功能:對兩個復數進行乘法運算
- 輸入參數:兩個以聯合體定義的復數a,b
- 輸出參數:a和b的乘積,以聯合體的形式輸出
- *******************************************************************/
- struct compx EE(struct compx a,struct compx b)
- {
- struct compx c;
- c.real=a.real*b.real-a.imag*b.imag;
- c.imag=a.real*b.imag+a.imag*b.real;
- return(c);
- }
- /*****************************************************************
- 函數原型:void FFT(struct compx *xin,int N)
- 函數功能:對輸入的復數組進行快速傅里葉變換(FFT)
- 輸入參數:*xin復數結構體組的首地址指針,struct型
- *****************************************************************/
- void FFT(struct compx *xin)
- {
- int f,m,nv2,nm1,i,k,l,j=0;
- struct compx u,w,t;
- nv2=FFT_N/2; //變址運算,即把自然順序變成倒位序,采用雷德算法
- nm1=FFT_N-1;
- for(i=0;i<nm1;i++)
- {
- if(i<j) //如果i<j,即進行變址
- {
- t=xin[j];
- xin[j]=xin[i];
- xin[i]=t;
- }
- k=nv2; //求j的下一個倒位序
-
- while(k<=j) //如果k<=j,表示j的最高位為1
- {
- j=j-k; //把最高位變成0
- k=k/2; //k/2,比較次高位,依次類推,逐個比較,直到某個位為0
- }
-
- j=j+k; //把0改為1
- }
-
- { //FFT運算核,使用蝶形運算完成FFT運算
- int le,lei,ip;
- f=FFT_N;
- for(l=1;(f=f/2)!=1;l++) //計算l的值,即計算蝶形級數
- ;
- for(m=1;m<=l;m++) // 控制蝶形結級數
- { //m表示第m級蝶形,l為蝶形級總數l=log(2)N
- le=2<<(m-1); //le蝶形結距離,即第m級蝶形的蝶形結相距le點
- lei=le/2; //同一蝶形結中參加運算的兩點的距離
- u.real=1.0; //u為蝶形結運算系數,初始值為1
- u.imag=0.0;
- w.real=XCos(PI/lei); //w為系數商,即當前系數與前一個系數的商
- w.imag=-XSin(PI/lei);
- for(j=0;j<=lei-1;j++) //控制計算不同種蝶形結,即計算系數不同的蝶形結
- {
- for(i=j;i<=FFT_N-1;i=i+le) //控制同一蝶形結運算,即計算系數相同蝶形結
- {
- ip=i+lei; //i,ip分別表示參加蝶形運算的兩個節點
- t=EE(xin[ip],u); //蝶形運算,詳見公式
- xin[ip].real=xin[i].real-t.real;
- xin[ip].imag=xin[i].imag-t.imag;
- xin[i].real=xin[i].real+t.real;
- xin[i].imag=xin[i].imag+t.imag;
- }
- u=EE(u,w); //改變系數,進行下一個蝶形運算
- }
- }
- }
- }
- u16 g_fft_index=0;
- u16 qsqrt(u32 a)
- {
- u32 rem = 0, root = 0, divisor = 0;
- u16 i;
- for(i=0; i<16; i++)
- {
- root <<= 1;
- rem = ((rem << 2) + (a>>30));
- a <<= 2;
- divisor = (root << 1) + 1;
- if(divisor <= rem)
- {
- rem -= divisor;
- root++;
- }
- }
- return root;
- }
- #define START_INDEX 10 //濾出低頻干擾
- u16 find_max_num_index(struct compx *data,u16 count)
- {
- u16 i=START_INDEX;
- u16 max_num_index = i;
- //struct compx temp=data[i];
- float temp = data[i].real;
- for(i=START_INDEX;i<count;i++)
- {
- if(temp < data[i].real)
- {
- temp = data[i].real;
- max_num_index = i;
- }
- }
- printf("max_num_index=%d\r\n",max_num_index);
- return max_num_index;
-
- }
- #define CORRECTED_VALUE 50 //粗略標定血液氧氣含量 ,精準數據需要大量測量
- void sp02_treated_fun(u16 max_index)
- {
- float sp02_num=0;
-
- delayl_init(); //延時函數初始化
- OLED_Init(); //初始化OLED
-
- printf("\r\n zhiliu s1=%f,s2=%f \r\n",s1[0].real,s2[0].real);
- printf("\r\n s1=%f,s2=%f \r\n",s1[max_index].real,s2[max_index].real);
- if((s1[max_index].real*s2[0].real)>(s2[max_index].real*s1[0].real)) //if ir>red sp02>75%
- {
- sp02_num = (s2[max_index].real*s1[0].real)/(s1[max_index].real*s2[0].real);
- printf("\r\nsp02_num : %f\r\n",sp02_num*100);
- printf("\r\n血氧含量為: %f\r\n",(1-sp02_num)*100+CORRECTED_VALUE);
-
- OLED_ShowString(0,0, "SpO2:",16);
- if((1-sp02_num)*100+CORRECTED_VALUE>99)
- OLED_ShowString(40,0, "99",16);
- else
- OLED_ShowNum(40,0,(1-sp02_num)*100+CORRECTED_VALUE,4,16);
- OLED_ShowString(80,0,"%",16);
- OLED_ShowString(0,30,"Heart Rate:",12);
- OLED_Refresh_Gram();//更新顯示到OLED
-
- }
- else // sp02<75%
- {
- printf("\r\n 嚴重缺氧! \r\n");
-
- OLED_ShowString(0,0, "SpO2:",16);
- OLED_ShowString(40,0,"ANOXIA!",16);
- OLED_ShowString(0,30,"Heart Rate:",12);
- OLED_Refresh_Gram();//更新顯示到OLED
- }
- }
- void test_max30100_fun(void)
- {
- u16 temp_num=0;
- u16 fifo_word_buff[15][2];
- u16 Heart_Rate=0;
- u16 s1_max_index=0;
- u16 s2_max_index=0;
-
- ……………………
- …………限于本文篇幅 余下代碼請從51黑下載附件…………
復制代碼
0.png (45.66 KB, 下載次數: 157)
下載附件
2017-10-31 17:08 上傳
所有資料51hei提供下載:
血氧心率.7z
(4.33 MB, 下載次數: 1349)
2021-8-19 01:09 上傳
點擊文件名下載附件
下載積分: 黑幣 -5
|