当前位置:   article > 正文

基于STM32以及HAL库的MAX30102模块使用+OLED显示(资源下载免费,在博主我的资源下载处)_max30102+oled

max30102+oled

一、关于Max30102模块的一些基本问题

(1)为何连接上电之后,模块的红灯仍然不会发光?

  很明确的说,在没有代码驱动的前提下,只是单纯给MAX30102模块上电的话,MAX30102模块上的红灯是不会亮的,必须要有I2C驱动,为其模块的寄存器写入相应的配置,才能够驱动红灯亮起(里面包括红光以及红外光)。

  那我们买回模块之后,如何确定模块的好坏,其实可以直接在购买物品的平台上向商家索要相应的资料,一般就包括有相应的代码可以直接使用,在接对线的情况下,一般就应该会亮灯了

  

(2)INT引脚能否不配置,没有配置能不能读取数值?

  先给出我的回答,其实不配置INT引脚或者是直接不使用,也是可以读取数值的,但是配置INT引脚也不难,为其配置为相应的外部中断引脚即可,而中断的产生,也并不是说当手指放上去才会产生中断的,产生中断也可以是每次采样周期结束时才会产生中断的。

(3)为何自己读取的数值波动大,不够稳定?

  在使用商家提供的代码或者自己移植的代码时,可能会有小伙伴发现,怎么数据波动仍然那么大,心率一直处于200多到500跳动,就是数值下不来,那么大概率是因为自己的手指接触到了排针,产生了阻抗,带来了干扰,所以尽量减少手指接触到排针,可以将排针用其他东西挡着,例如胶带之类的。

  当然MAX30102模块寄存器配置,I2C的写法等都有可能造成波动大的问题。

二、OLED+MAX30102接线

MAX30102OLED使用USART3
SCLPB8SCLPB6PB10
SDAPB9SDAPB7PB11
INTPB4

三、关于MAX30102的关键代码

max30102.c

  1. #include "max30102.h"
  2. #include "myiic.h"
  3. #include "gpio.h"
  4. /*MAX30102:
  5. VCC<->3.3V
  6. GND<->GND
  7. SCL<->PB8
  8. SDA<->PB9
  9. IM<->PB4
  10. */
  11. uint32_t aun_ir_buffer[500]; //IR LED sensor data
  12. int32_t n_ir_buffer_length; //data length
  13. uint32_t aun_red_buffer[500]; //Red LED sensor data
  14. int32_t n_sp02; //SPO2 value
  15. int8_t ch_spo2_valid; //indicator to show if the SP02 calculation is valid
  16. int32_t n_heart_rate; //heart rate value
  17. int8_t ch_hr_valid; //indicator to show if the heart rate calculation is valid
  18. uint8_t uch_dummy;
  19. //variables to calculate the on-board LED brightness that reflects the heartbeats
  20. uint32_t un_min, un_max, un_prev_data;
  21. int i;
  22. int32_t n_brightness;
  23. float f_temp;
  24. uint8_t temp_num=0;
  25. uint8_t temp[6];
  26. uint8_t str[100];
  27. uint8_t dis_hr=0,dis_spo2=0;
  28. int flag1=0;
  29. uint16_t sum[12];
  30. uint16_t sum1[12];
  31. int j,k,temp1,temp2;
  32. int progess,flag3=0;
  33. #define MAX_BRIGHTNESS 255
  34. uint8_t max30102_Bus_Write(uint8_t Register_Address, uint8_t Word_Data)
  35. {
  36. /* 采用串行EEPROM随即读取指令序列,连续读取若干字节 */
  37. /* 第1步:发起I2C总线启动信号 */
  38. IIC_Start();
  39. /* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
  40. IIC_Send_Byte(max30102_WR_address | I2C_WR); /* 此处是写指令 */
  41. /* 第3步:发送ACK */
  42. if (IIC_Wait_Ack() != 0)
  43. {
  44. goto cmd_fail; /* EEPROM器件无应答 */
  45. }
  46. /* 第4步:发送字节地址 */
  47. IIC_Send_Byte(Register_Address);
  48. if (IIC_Wait_Ack() != 0)
  49. {
  50. goto cmd_fail; /* EEPROM器件无应答 */
  51. }
  52. /* 第5步:开始写入数据 */
  53. IIC_Send_Byte(Word_Data);
  54. /* 第6步:发送ACK */
  55. if (IIC_Wait_Ack() != 0)
  56. {
  57. goto cmd_fail; /* EEPROM器件无应答 */
  58. }
  59. /* 发送I2C总线停止信号 */
  60. IIC_Stop();
  61. return 1; /* 执行成功 */
  62. cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
  63. /* 发送I2C总线停止信号 */
  64. IIC_Stop();
  65. return 0;
  66. }
  67. uint8_t max30102_Bus_Read(uint8_t Register_Address)
  68. {
  69. uint8_t data;
  70. /* 第1步:发起I2C总线启动信号 */
  71. IIC_Start();
  72. /* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
  73. IIC_Send_Byte(max30102_WR_address | I2C_WR); /* 此处是写指令 */
  74. /* 第3步:发送ACK */
  75. if (IIC_Wait_Ack() != 0)
  76. {
  77. goto cmd_fail; /* EEPROM器件无应答 */
  78. }
  79. /* 第4步:发送字节地址, */
  80. IIC_Send_Byte((uint8_t)Register_Address);
  81. if (IIC_Wait_Ack() != 0)
  82. {
  83. goto cmd_fail; /* EEPROM器件无应答 */
  84. }
  85. /* 第6步:重新启动I2C总线。下面开始读取数据 */
  86. IIC_Start();
  87. /* 第7步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
  88. IIC_Send_Byte(max30102_WR_address | I2C_RD); /* 此处是读指令 */
  89. /* 第8步:发送ACK */
  90. if (IIC_Wait_Ack() != 0)
  91. {
  92. goto cmd_fail; /* EEPROM器件无应答 */
  93. }
  94. /* 第9步:读取数据 */
  95. {
  96. data = IIC_Read_Byte(0); /* 读1个字节 */
  97. IIC_NAck(); /* 最后1个字节读完后,CPU产生NACK信号(驱动SDA = 1) */
  98. }
  99. /* 发送I2C总线停止信号 */
  100. IIC_Stop();
  101. return data; /* 执行成功 返回data值 */
  102. cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
  103. /* 发送I2C总线停止信号 */
  104. IIC_Stop();
  105. return 0;
  106. }
  107. void max30102_FIFO_ReadWords(uint8_t Register_Address,uint16_t Word_Data[][2],uint8_t count)
  108. {
  109. uint8_t i=0;
  110. uint8_t no = count;
  111. uint8_t data1, data2;
  112. /* 第1步:发起I2C总线启动信号 */
  113. IIC_Start();
  114. /* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
  115. IIC_Send_Byte(max30102_WR_address | I2C_WR); /* 此处是写指令 */
  116. /* 第3步:发送ACK */
  117. if (IIC_Wait_Ack() != 0)
  118. {
  119. goto cmd_fail; /* EEPROM器件无应答 */
  120. }
  121. /* 第4步:发送字节地址, */
  122. IIC_Send_Byte((uint8_t)Register_Address);
  123. if (IIC_Wait_Ack() != 0)
  124. {
  125. goto cmd_fail; /* EEPROM器件无应答 */
  126. }
  127. /* 第6步:重新启动I2C总线。下面开始读取数据 */
  128. IIC_Start();
  129. /* 第7步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
  130. IIC_Send_Byte(max30102_WR_address | I2C_RD); /* 此处是读指令 */
  131. /* 第8步:发送ACK */
  132. if (IIC_Wait_Ack() != 0)
  133. {
  134. goto cmd_fail; /* EEPROM器件无应答 */
  135. }
  136. /* 第9步:读取数据 */
  137. while (no)
  138. {
  139. data1 = IIC_Read_Byte(0);
  140. IIC_Ack();
  141. data2 = IIC_Read_Byte(0);
  142. IIC_Ack();
  143. Word_Data[i][0] = (((uint16_t)data1 << 8) | data2); //
  144. data1 = IIC_Read_Byte(0);
  145. IIC_Ack();
  146. data2 = IIC_Read_Byte(0);
  147. if(1==no)
  148. IIC_NAck(); /* 最后1个字节读完后,CPU产生NACK信号(驱动SDA = 1) */
  149. else
  150. IIC_Ack();
  151. Word_Data[i][1] = (((uint16_t)data1 << 8) | data2);
  152. no--;
  153. i++;
  154. }
  155. /* 发送I2C总线停止信号 */
  156. IIC_Stop();
  157. cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
  158. /* 发送I2C总线停止信号 */
  159. IIC_Stop();
  160. }
  161. void max30102_FIFO_ReadBytes(uint8_t Register_Address,uint8_t* Data)
  162. {
  163. max30102_Bus_Read(REG_INTR_STATUS_1);
  164. max30102_Bus_Read(REG_INTR_STATUS_2);
  165. /* 第1步:发起I2C总线启动信号 */
  166. IIC_Start();
  167. /* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
  168. IIC_Send_Byte(max30102_WR_address | I2C_WR); /* 此处是写指令 */
  169. /* 第3步:发送ACK */
  170. if (IIC_Wait_Ack() != 0)
  171. {
  172. goto cmd_fail; /* EEPROM器件无应答 */
  173. }
  174. /* 第4步:发送字节地址, */
  175. IIC_Send_Byte((uint8_t)Register_Address);
  176. if (IIC_Wait_Ack() != 0)
  177. {
  178. goto cmd_fail; /* EEPROM器件无应答 */
  179. }
  180. /* 第6步:重新启动I2C总线。下面开始读取数据 */
  181. IIC_Start();
  182. /* 第7步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
  183. IIC_Send_Byte(max30102_WR_address | I2C_RD); /* 此处是读指令 */
  184. /* 第8步:发送ACK */
  185. if (IIC_Wait_Ack() != 0)
  186. {
  187. goto cmd_fail; /* EEPROM器件无应答 */
  188. }
  189. /* 第9步:读取数据 */
  190. Data[0] = IIC_Read_Byte(1);
  191. Data[1] = IIC_Read_Byte(1);
  192. Data[2] = IIC_Read_Byte(1);
  193. Data[3] = IIC_Read_Byte(1);
  194. Data[4] = IIC_Read_Byte(1);
  195. Data[5] = IIC_Read_Byte(0);
  196. /* 最后1个字节读完后,CPU产生NACK信号(驱动SDA = 1) */
  197. /* 发送I2C总线停止信号 */
  198. IIC_Stop();
  199. cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
  200. /* 发送I2C总线停止信号 */
  201. IIC_Stop();
  202. // uint8_t i;
  203. // uint8_t fifo_wr_ptr;
  204. // uint8_t firo_rd_ptr;
  205. // uint8_t number_tp_read;
  206. // //Get the FIFO_WR_PTR
  207. // fifo_wr_ptr = max30102_Bus_Read(REG_FIFO_WR_PTR);
  208. // //Get the FIFO_RD_PTR
  209. // firo_rd_ptr = max30102_Bus_Read(REG_FIFO_RD_PTR);
  210. //
  211. // number_tp_read = fifo_wr_ptr - firo_rd_ptr;
  212. //
  213. // //for(i=0;i<number_tp_read;i++){
  214. // if(number_tp_read>0){
  215. // IIC_ReadBytes(max30102_WR_address,REG_FIFO_DATA,Data,6);
  216. // }
  217. //max30102_Bus_Write(REG_FIFO_RD_PTR,fifo_wr_ptr);
  218. }
  219. void max30102_init(void)
  220. {
  221. // GPIO_InitTypeDef GPIO_InitStructure;
  222. // RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
  223. // GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
  224. // GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
  225. // GPIO_Init(GPIOB, &GPIO_InitStructure);
  226. // IIC_Init();
  227. max30102_reset();
  228. // max30102_Bus_Write(REG_MODE_CONFIG, 0x0b); //mode configuration : temp_en[3] MODE[2:0]=010 HR only enabled 011 SP02 enabled
  229. // max30102_Bus_Write(REG_INTR_STATUS_2, 0xF0); //open all of interrupt
  230. // max30102_Bus_Write(REG_INTR_STATUS_1, 0x00); //all interrupt clear
  231. // max30102_Bus_Write(REG_INTR_ENABLE_2, 0x02); //DIE_TEMP_RDY_EN
  232. // max30102_Bus_Write(REG_TEMP_CONFIG, 0x01); //SET TEMP_EN
  233. // max30102_Bus_Write(REG_SPO2_CONFIG, 0x47); //SPO2_SR[4:2]=001 100 per second LED_PW[1:0]=11 16BITS
  234. // max30102_Bus_Write(REG_LED1_PA, 0x47);
  235. // max30102_Bus_Write(REG_LED2_PA, 0x47);
  236. max30102_Bus_Write(REG_INTR_ENABLE_1,0xc0); // INTR setting
  237. max30102_Bus_Write(REG_INTR_ENABLE_2,0x00);
  238. max30102_Bus_Write(REG_FIFO_WR_PTR,0x00); //FIFO_WR_PTR[4:0]
  239. max30102_Bus_Write(REG_OVF_COUNTER,0x00); //OVF_COUNTER[4:0]
  240. max30102_Bus_Write(REG_FIFO_RD_PTR,0x00); //FIFO_RD_PTR[4:0]
  241. max30102_Bus_Write(REG_FIFO_CONFIG,0x0f); //sample avg = 1, fifo rollover=false, fifo almost full = 17
  242. max30102_Bus_Write(REG_MODE_CONFIG,0x03); //0x02 for Red only, 0x03 for SpO2 mode 0x07 multimode LED
  243. max30102_Bus_Write(REG_SPO2_CONFIG,0x27); // SPO2_ADC range = 4096nA, SPO2 sample rate (100 Hz), LED pulseWidth (400uS)
  244. max30102_Bus_Write(REG_LED1_PA,0x24); //Choose value for ~ 7mA for LED1
  245. max30102_Bus_Write(REG_LED2_PA,0x24); // Choose value for ~ 7mA for LED2
  246. max30102_Bus_Write(REG_PILOT_PA,0x7f); // Choose value for ~ 25mA for Pilot LED
  247. }
  248. void max30102_reset(void)
  249. {
  250. max30102_Bus_Write(REG_MODE_CONFIG,0x40);
  251. max30102_Bus_Write(REG_MODE_CONFIG,0x40);
  252. }
  253. void maxim_max30102_write_reg(uint8_t uch_addr, uint8_t uch_data)
  254. {
  255. IIC_Write_One_Byte(I2C_WRITE_ADDR,uch_addr,uch_data);
  256. }
  257. void maxim_max30102_read_reg(uint8_t uch_addr, uint8_t *puch_data)
  258. {
  259. IIC_Read_One_Byte(I2C_WRITE_ADDR,uch_addr,puch_data);
  260. }
  261. void maxim_max30102_read_fifo(uint32_t *pun_red_led, uint32_t *pun_ir_led)
  262. {
  263. uint32_t un_temp;
  264. unsigned char uch_temp;
  265. char ach_i2c_data[6];
  266. *pun_red_led=0;
  267. *pun_ir_led=0;
  268. //read and clear status register
  269. maxim_max30102_read_reg(REG_INTR_STATUS_1, &uch_temp);
  270. maxim_max30102_read_reg(REG_INTR_STATUS_2, &uch_temp);
  271. IIC_ReadBytes(I2C_WRITE_ADDR,REG_FIFO_DATA,(uint8_t *)ach_i2c_data,6);
  272. un_temp=(unsigned char) ach_i2c_data[0];
  273. un_temp<<=16;
  274. *pun_red_led+=un_temp;
  275. un_temp=(unsigned char) ach_i2c_data[1];
  276. un_temp<<=8;
  277. *pun_red_led+=un_temp;
  278. un_temp=(unsigned char) ach_i2c_data[2];
  279. *pun_red_led+=un_temp;
  280. un_temp=(unsigned char) ach_i2c_data[3];
  281. un_temp<<=16;
  282. *pun_ir_led+=un_temp;
  283. un_temp=(unsigned char) ach_i2c_data[4];
  284. un_temp<<=8;
  285. *pun_ir_led+=un_temp;
  286. un_temp=(unsigned char) ach_i2c_data[5];
  287. *pun_ir_led+=un_temp;
  288. *pun_red_led&=0x03FFFF; //Mask MSB [23:18]
  289. *pun_ir_led&=0x03FFFF; //Mask MSB [23:18]
  290. }
  291. void dis_DrawCurve(uint32_t* data,uint8_t x)
  292. {
  293. uint16_t i;
  294. uint32_t max=0,min=262144;
  295. uint32_t temp;
  296. uint32_t compress;
  297. for(i=0;i<128*2;i++)
  298. {
  299. if(data[i]>max)
  300. {
  301. max = data[i];
  302. }
  303. if(data[i]<min)
  304. {
  305. min = data[i];
  306. }
  307. }
  308. compress = (max-min)/20;
  309. for(i=0;i<128;i++)
  310. {
  311. temp = data[i*2] + data[i*2+1];
  312. temp/=2;
  313. temp -= min;
  314. temp/=compress;
  315. if(temp>20)temp=20;
  316. }
  317. }
  318. void MAX30102_data_set()
  319. {
  320. // printf("\r\n MAX30102 init \r\n");
  321. un_min=0x3FFFF;
  322. un_max=0;
  323. n_ir_buffer_length=500; //buffer length of 100 stores 5 seconds of samples running at 100sps
  324. //read the first 500 samples, and determine the signal range
  325. for(i=0;i<n_ir_buffer_length;i++)
  326. {
  327. while(MAX30102_INT==1); //wait until the interrupt pin asserts
  328. max30102_FIFO_ReadBytes(REG_FIFO_DATA,temp);
  329. aun_red_buffer[i] = (long)((long)((long)temp[0]&0x03)<<16) | (long)temp[1]<<8 | (long)temp[2]; // Combine values to get the actual number
  330. aun_ir_buffer[i] = (long)((long)((long)temp[3] & 0x03)<<16) |(long)temp[4]<<8 | (long)temp[5]; // Combine values to get the actual number
  331. if(un_min>aun_red_buffer[i])
  332. un_min=aun_red_buffer[i]; //update signal min
  333. if(un_max<aun_red_buffer[i])
  334. un_max=aun_red_buffer[i]; //update signal max
  335. }
  336. un_prev_data=aun_red_buffer[i];
  337. //calculate heart rate and SpO2 after first 500 samples (first 5 seconds of samples)
  338. maxim_heart_rate_and_oxygen_saturation(aun_ir_buffer, n_ir_buffer_length, aun_red_buffer, &n_sp02, &ch_spo2_valid, &n_heart_rate, &ch_hr_valid);
  339. }
  340. void MAX30102_get(uint8_t *hr,uint8_t *spo2)
  341. {
  342. i=0;
  343. un_min=0x3FFFF;
  344. un_max=0;
  345. //dumping the first 100 sets of samples in the memory and shift the last 400 sets of samples to the top
  346. for(i=100;i<500;i++)
  347. {
  348. aun_red_buffer[i-100]=aun_red_buffer[i];
  349. aun_ir_buffer[i-100]=aun_ir_buffer[i];
  350. //update the signal min and max
  351. if(un_min>aun_red_buffer[i])
  352. un_min=aun_red_buffer[i];
  353. if(un_max<aun_red_buffer[i])
  354. un_max=aun_red_buffer[i];
  355. }
  356. //take 100 sets of samples before calculating the heart rate.
  357. for(i=400;i<500;i++)
  358. {
  359. un_prev_data=aun_red_buffer[i-1];
  360. while(MAX30102_INT==1);
  361. max30102_FIFO_ReadBytes(REG_FIFO_DATA,temp);
  362. aun_red_buffer[i] = (long)((long)((long)temp[0]&0x03)<<16) | (long)temp[1]<<8 | (long)temp[2]; // Combine values to get the actual number
  363. aun_ir_buffer[i] = (long)((long)((long)temp[3] & 0x03)<<16) |(long)temp[4]<<8 | (long)temp[5]; // Combine values to get the actual number
  364. if(aun_red_buffer[i]>un_prev_data)
  365. {
  366. f_temp=aun_red_buffer[i]-un_prev_data;
  367. f_temp/=(un_max-un_min);
  368. f_temp*=MAX_BRIGHTNESS;
  369. n_brightness-=(int)f_temp;
  370. if(n_brightness<0)
  371. n_brightness=0;
  372. }
  373. else
  374. {
  375. f_temp=un_prev_data-aun_red_buffer[i];
  376. f_temp/=(un_max-un_min);
  377. f_temp*=MAX_BRIGHTNESS;
  378. n_brightness+=(int)f_temp;
  379. if(n_brightness>MAX_BRIGHTNESS)
  380. n_brightness=MAX_BRIGHTNESS;
  381. }
  382. printf("red=");
  383. printf("%i", aun_red_buffer[i]);
  384. printf(", ir=");
  385. printf("%i", aun_ir_buffer[i]);
  386. printf(", HR=%i, ", n_heart_rate);
  387. printf("HRvalid=%i, ", ch_hr_valid);
  388. printf("SpO2=%i, ", n_sp02);
  389. printf("SPO2Valid=%i\n\r", ch_spo2_valid);
  390. //当心率满足一下条件后,就读取一次
  391. if(ch_hr_valid == 1 && n_heart_rate<150&&n_heart_rate>60&&aun_red_buffer[i]>80000&&ch_spo2_valid==1&&aun_ir_buffer[i]>120000)
  392. {
  393. sum[j] = n_heart_rate;
  394. sum1[j]=n_sp02;
  395. }
  396. //读取11个数值,第一个舍弃,比较后面10个数据,选取最小值作为最后的心率
  397. if(j==11)
  398. {
  399. for(j = 1;j <10;j++)
  400. {
  401. for(k = j+1;k <10;k++)
  402. {
  403. if(sum[k] < sum[j])
  404. {
  405. temp1 = sum[j];
  406. sum[j] = sum[k];
  407. sum[k] = temp1;
  408. }
  409. if(sum1[k] < sum1[j])
  410. {
  411. temp2 = sum1[j];
  412. sum1[j] = sum1[k];
  413. sum1[k] = temp2;
  414. }
  415. }
  416. }
  417. dis_hr=sum[1];
  418. dis_spo2=sum1[8];
  419. j = 0;
  420. }
  421. *hr = dis_hr;
  422. *spo2 = dis_spo2;
  423. if(progess==100)
  424. flag3=1;
  425. }
  426. if(sum[j]!=0)
  427. {
  428. if(progess!=100&&j!=0)
  429. progess+=10;
  430. j++;
  431. }
  432. maxim_heart_rate_and_oxygen_saturation(aun_ir_buffer, n_ir_buffer_length, aun_red_buffer, &n_sp02, &ch_spo2_valid, &n_heart_rate, &ch_hr_valid);
  433. //send samples and calculation result to terminal program through UART
  434. //显示进度,到100则是读取成功
  435. OLED_ShowNum(90,0,progess,3,16);
  436. //红光在上,红外在下
  437. dis_DrawCurve(aun_red_buffer,20);
  438. dis_DrawCurve(aun_ir_buffer,0);
  439. }

max30102.h

  1. #ifndef __MAX30102_H
  2. #define __MAX30102_H
  3. #include "main.h"
  4. //
  5. #define MAX30102_INT HAL_GPIO_ReadPin(GPIOB,MAX30102_INT_Pin)
  6. #define I2C_WR 0 /* 写控制bit */
  7. #define I2C_RD 1 /* 读控制bit */
  8. #define max30102_WR_address 0xAE
  9. #define I2C_WRITE_ADDR 0xAE
  10. #define I2C_READ_ADDR 0xAF
  11. //register addresses
  12. #define REG_INTR_STATUS_1 0x00
  13. #define REG_INTR_STATUS_2 0x01
  14. #define REG_INTR_ENABLE_1 0x02
  15. #define REG_INTR_ENABLE_2 0x03
  16. #define REG_FIFO_WR_PTR 0x04
  17. #define REG_OVF_COUNTER 0x05
  18. #define REG_FIFO_RD_PTR 0x06
  19. #define REG_FIFO_DATA 0x07
  20. #define REG_FIFO_CONFIG 0x08
  21. #define REG_MODE_CONFIG 0x09
  22. #define REG_SPO2_CONFIG 0x0A
  23. #define REG_LED1_PA 0x0C
  24. #define REG_LED2_PA 0x0D
  25. #define REG_PILOT_PA 0x10
  26. #define REG_MULTI_LED_CTRL1 0x11
  27. #define REG_MULTI_LED_CTRL2 0x12
  28. #define REG_TEMP_INTR 0x1F
  29. #define REG_TEMP_FRAC 0x20
  30. #define REG_TEMP_CONFIG 0x21
  31. #define REG_PROX_INT_THRESH 0x30
  32. #define REG_REV_ID 0xFE
  33. #define REG_PART_ID 0xFF
  34. void max30102_init(void);
  35. void max30102_reset(void);
  36. uint8_t max30102_Bus_Write(uint8_t Register_Address, uint8_t Word_Data);
  37. uint8_t max30102_Bus_Read(uint8_t Register_Address);
  38. void max30102_FIFO_ReadWords(uint8_t Register_Address,uint16_t Word_Data[][2],uint8_t count);
  39. void max30102_FIFO_ReadBytes(uint8_t Register_Address,uint8_t* Data);
  40. void maxim_max30102_write_reg(uint8_t uch_addr, uint8_t uch_data);
  41. void maxim_max30102_read_reg(uint8_t uch_addr, uint8_t *puch_data);
  42. void maxim_max30102_read_fifo(uint32_t *pun_red_led, uint32_t *pun_ir_led);
  43. void dis_DrawCurve(uint32_t* data,uint8_t x);
  44. void MAX30102_get(uint8_t *hr,uint8_t *spo2);
  45. void MAX30102_data_set();
  46. #endif

algorithm.c

  1. #include "algorithm.h"
  2. #include "main.h"
  3. void maxim_heart_rate_and_oxygen_saturation(uint32_t *pun_ir_buffer, int32_t n_ir_buffer_length, uint32_t *pun_red_buffer, int32_t *pn_spo2, int8_t *pch_spo2_valid,
  4. int32_t *pn_heart_rate, int8_t *pch_hr_valid)
  5. /**
  6. * \brief Calculate the heart rate and SpO2 level
  7. * \par Details
  8. * By detecting peaks of PPG cycle and corresponding AC/DC of red/infra-red signal, the ratio for the SPO2 is computed.
  9. * Since this algorithm is aiming for Arm M0/M3. formaula for SPO2 did not achieve the accuracy due to register overflow.
  10. * Thus, accurate SPO2 is precalculated and save longo uch_spo2_table[] per each ratio.
  11. *
  12. * \param[in] *pun_ir_buffer - IR sensor data buffer
  13. * \param[in] n_ir_buffer_length - IR sensor data buffer length
  14. * \param[in] *pun_red_buffer - Red sensor data buffer
  15. * \param[out] *pn_spo2 - Calculated SpO2 value
  16. * \param[out] *pch_spo2_valid - 1 if the calculated SpO2 value is valid
  17. * \param[out] *pn_heart_rate - Calculated heart rate value
  18. * \param[out] *pch_hr_valid - 1 if the calculated heart rate value is valid
  19. *
  20. * \retval None
  21. */
  22. {
  23. uint32_t un_ir_mean ,un_only_once ;
  24. int32_t k ,n_i_ratio_count;
  25. int32_t i, s, m, n_exact_ir_valley_locs_count ,n_middle_idx;
  26. int32_t n_th1, n_npks,n_c_min;
  27. int32_t an_ir_valley_locs[15] ;
  28. int32_t an_exact_ir_valley_locs[15] ;
  29. int32_t an_dx_peak_locs[15] ;
  30. int32_t n_peak_interval_sum;
  31. int32_t n_y_ac, n_x_ac;
  32. int32_t n_spo2_calc;
  33. int32_t n_y_dc_max, n_x_dc_max;
  34. int32_t n_y_dc_max_idx, n_x_dc_max_idx;
  35. int32_t an_ratio[5],n_ratio_average;
  36. int32_t n_nume, n_denom ;
  37. // remove DC of ir signal
  38. un_ir_mean =0;
  39. for (k=0 ; k<n_ir_buffer_length ; k++ ) un_ir_mean += pun_ir_buffer[k] ;
  40. un_ir_mean =un_ir_mean/n_ir_buffer_length ;
  41. for (k=0 ; k<n_ir_buffer_length ; k++ ) an_x[k] = pun_ir_buffer[k] - un_ir_mean ;
  42. // 4 pt Moving Average
  43. for(k=0; k< BUFFER_SIZE-MA4_SIZE; k++){
  44. n_denom= ( an_x[k]+an_x[k+1]+ an_x[k+2]+ an_x[k+3]);
  45. an_x[k]= n_denom/(int32_t)4;
  46. }
  47. // get difference of smoothed IR signal
  48. for( k=0; k<BUFFER_SIZE-MA4_SIZE-1; k++)
  49. an_dx[k]= (an_x[k+1]- an_x[k]);
  50. // 2-pt Moving Average to an_dx
  51. for(k=0; k< BUFFER_SIZE-MA4_SIZE-2; k++){
  52. an_dx[k] = ( an_dx[k]+an_dx[k+1])/2 ;
  53. }
  54. // hamming window
  55. // flip wave form so that we can detect valley with peak detector
  56. for ( i=0 ; i<BUFFER_SIZE-HAMMING_SIZE-MA4_SIZE-2 ;i++){
  57. s= 0;
  58. for( k=i; k<i+ HAMMING_SIZE ;k++){
  59. s -= an_dx[k] *auw_hamm[k-i] ;
  60. }
  61. an_dx[i]= s/ (int32_t)1146; // divide by sum of auw_hamm
  62. }
  63. n_th1=0; // threshold calculation
  64. for ( k=0 ; k<BUFFER_SIZE-HAMMING_SIZE ;k++){
  65. n_th1 += ((an_dx[k]>0)? an_dx[k] : ((int32_t)0-an_dx[k])) ;
  66. }
  67. n_th1= n_th1/ ( BUFFER_SIZE-HAMMING_SIZE);
  68. // peak location is acutally index for sharpest location of raw signal since we flipped the signal
  69. maxim_find_peaks( an_dx_peak_locs, &n_npks, an_dx, BUFFER_SIZE-HAMMING_SIZE, n_th1, 8, 5 );//peak_height, peak_distance, max_num_peaks
  70. n_peak_interval_sum =0;
  71. if (n_npks>=2){
  72. for (k=1; k<n_npks; k++)
  73. n_peak_interval_sum += (an_dx_peak_locs[k]-an_dx_peak_locs[k -1]);
  74. n_peak_interval_sum=n_peak_interval_sum/(n_npks-1);
  75. *pn_heart_rate=(int32_t)(6000/n_peak_interval_sum);// beats per minutes
  76. *pch_hr_valid = 1;
  77. }
  78. else {
  79. *pn_heart_rate = -999;
  80. *pch_hr_valid = 0;
  81. }
  82. for ( k=0 ; k<n_npks ;k++)
  83. an_ir_valley_locs[k]=an_dx_peak_locs[k]+HAMMING_SIZE/2;
  84. // raw value : RED(=y) and IR(=X)
  85. // we need to assess DC and AC value of ir and red PPG.
  86. for (k=0 ; k<n_ir_buffer_length ; k++ ) {
  87. an_x[k] = pun_ir_buffer[k] ;
  88. an_y[k] = pun_red_buffer[k] ;
  89. }
  90. // find precise min near an_ir_valley_locs
  91. n_exact_ir_valley_locs_count =0;
  92. for(k=0 ; k<n_npks ;k++){
  93. un_only_once =1;
  94. m=an_ir_valley_locs[k];
  95. n_c_min= 16777216;//2^24;
  96. if (m+5 < BUFFER_SIZE-HAMMING_SIZE && m-5 >0){
  97. for(i= m-5;i<m+5; i++)
  98. if (an_x[i]<n_c_min){
  99. if (un_only_once >0){
  100. un_only_once =0;
  101. }
  102. n_c_min= an_x[i] ;
  103. an_exact_ir_valley_locs[k]=i;
  104. }
  105. if (un_only_once ==0)
  106. n_exact_ir_valley_locs_count ++ ;
  107. }
  108. }
  109. if (n_exact_ir_valley_locs_count <2 ){
  110. *pn_spo2 = -999 ; // do not use SPO2 since signal ratio is out of range
  111. *pch_spo2_valid = 0;
  112. return;
  113. }
  114. // 4 pt MA
  115. for(k=0; k< BUFFER_SIZE-MA4_SIZE; k++){
  116. an_x[k]=( an_x[k]+an_x[k+1]+ an_x[k+2]+ an_x[k+3])/(int32_t)4;
  117. an_y[k]=( an_y[k]+an_y[k+1]+ an_y[k+2]+ an_y[k+3])/(int32_t)4;
  118. }
  119. //using an_exact_ir_valley_locs , find ir-red DC andir-red AC for SPO2 calibration ratio
  120. //finding AC/DC maximum of raw ir * red between two valley locations
  121. n_ratio_average =0;
  122. n_i_ratio_count =0;
  123. for(k=0; k< 5; k++) an_ratio[k]=0;
  124. for (k=0; k< n_exact_ir_valley_locs_count; k++){
  125. if (an_exact_ir_valley_locs[k] > BUFFER_SIZE ){
  126. *pn_spo2 = -999 ; // do not use SPO2 since valley loc is out of range
  127. *pch_spo2_valid = 0;
  128. return;
  129. }
  130. }
  131. // find max between two valley locations
  132. // and use ratio betwen AC compoent of Ir & Red and DC compoent of Ir & Red for SPO2
  133. for (k=0; k< n_exact_ir_valley_locs_count-1; k++){
  134. n_y_dc_max= -16777216 ;
  135. n_x_dc_max= - 16777216;
  136. if (an_exact_ir_valley_locs[k+1]-an_exact_ir_valley_locs[k] >10){
  137. for (i=an_exact_ir_valley_locs[k]; i< an_exact_ir_valley_locs[k+1]; i++){
  138. if (an_x[i]> n_x_dc_max) {n_x_dc_max =an_x[i];n_x_dc_max_idx =i; }
  139. if (an_y[i]> n_y_dc_max) {n_y_dc_max =an_y[i];n_y_dc_max_idx=i;}
  140. }
  141. n_y_ac= (an_y[an_exact_ir_valley_locs[k+1]] - an_y[an_exact_ir_valley_locs[k] ] )*(n_y_dc_max_idx -an_exact_ir_valley_locs[k]); //red
  142. n_y_ac= an_y[an_exact_ir_valley_locs[k]] + n_y_ac/ (an_exact_ir_valley_locs[k+1] - an_exact_ir_valley_locs[k]) ;
  143. n_y_ac= an_y[n_y_dc_max_idx] - n_y_ac; // subracting linear DC compoenents from raw
  144. n_x_ac= (an_x[an_exact_ir_valley_locs[k+1]] - an_x[an_exact_ir_valley_locs[k] ] )*(n_x_dc_max_idx -an_exact_ir_valley_locs[k]); // ir
  145. n_x_ac= an_x[an_exact_ir_valley_locs[k]] + n_x_ac/ (an_exact_ir_valley_locs[k+1] - an_exact_ir_valley_locs[k]);
  146. n_x_ac= an_x[n_y_dc_max_idx] - n_x_ac; // subracting linear DC compoenents from raw
  147. n_nume=( n_y_ac *n_x_dc_max)>>7 ; //prepare X100 to preserve floating value
  148. n_denom= ( n_x_ac *n_y_dc_max)>>7;
  149. if (n_denom>0 && n_i_ratio_count <5 && n_nume != 0)
  150. {
  151. an_ratio[n_i_ratio_count]= (n_nume*100)/n_denom ; //formular is ( n_y_ac *n_x_dc_max) / ( n_x_ac *n_y_dc_max) ;
  152. n_i_ratio_count++;
  153. }
  154. }
  155. }
  156. maxim_sort_ascend(an_ratio, n_i_ratio_count);
  157. n_middle_idx= n_i_ratio_count/2;
  158. if (n_middle_idx >1)
  159. n_ratio_average =( an_ratio[n_middle_idx-1] +an_ratio[n_middle_idx])/2; // use median
  160. else
  161. n_ratio_average = an_ratio[n_middle_idx ];
  162. if( n_ratio_average>2 && n_ratio_average <184){
  163. n_spo2_calc= uch_spo2_table[n_ratio_average] ;
  164. *pn_spo2 = n_spo2_calc ;
  165. *pch_spo2_valid = 1;// float_SPO2 = -45.060*n_ratio_average* n_ratio_average/10000 + 30.354 *n_ratio_average/100 + 94.845 ; // for comparison with table
  166. }
  167. else{
  168. *pn_spo2 = -999 ; // do not use SPO2 since signal ratio is out of range
  169. *pch_spo2_valid = 0;
  170. }
  171. }
  172. void maxim_find_peaks(int32_t *pn_locs, int32_t *pn_npks, int32_t *pn_x, int32_t n_size, int32_t n_min_height, int32_t n_min_distance, int32_t n_max_num)
  173. /**
  174. * \brief Find peaks
  175. * \par Details
  176. * Find at most MAX_NUM peaks above MIN_HEIGHT separated by at least MIN_DISTANCE
  177. *
  178. * \retval None
  179. */
  180. {
  181. maxim_peaks_above_min_height( pn_locs, pn_npks, pn_x, n_size, n_min_height );
  182. maxim_remove_close_peaks( pn_locs, pn_npks, pn_x, n_min_distance );
  183. *pn_npks = min( *pn_npks, n_max_num );
  184. }
  185. void maxim_peaks_above_min_height(int32_t *pn_locs, int32_t *pn_npks, int32_t *pn_x, int32_t n_size, int32_t n_min_height)
  186. /**
  187. * \brief Find peaks above n_min_height
  188. * \par Details
  189. * Find all peaks above MIN_HEIGHT
  190. *
  191. * \retval None
  192. */
  193. {
  194. int32_t i = 1, n_width;
  195. *pn_npks = 0;
  196. while (i < n_size-1){
  197. if (pn_x[i] > n_min_height && pn_x[i] > pn_x[i-1]){ // find left edge of potential peaks
  198. n_width = 1;
  199. while (i+n_width < n_size && pn_x[i] == pn_x[i+n_width]) // find flat peaks
  200. n_width++;
  201. if (pn_x[i] > pn_x[i+n_width] && (*pn_npks) < 15 ){ // find right edge of peaks
  202. pn_locs[(*pn_npks)++] = i;
  203. // for flat peaks, peak location is left edge
  204. i += n_width+1;
  205. }
  206. else
  207. i += n_width;
  208. }
  209. else
  210. i++;
  211. }
  212. }
  213. void maxim_remove_close_peaks(int32_t *pn_locs, int32_t *pn_npks, int32_t *pn_x, int32_t n_min_distance)
  214. /**
  215. * \brief Remove peaks
  216. * \par Details
  217. * Remove peaks separated by less than MIN_DISTANCE
  218. *
  219. * \retval None
  220. */
  221. {
  222. int32_t i, j, n_old_npks, n_dist;
  223. /* Order peaks from large to small */
  224. maxim_sort_indices_descend( pn_x, pn_locs, *pn_npks );
  225. for ( i = -1; i < *pn_npks; i++ ){
  226. n_old_npks = *pn_npks;
  227. *pn_npks = i+1;
  228. for ( j = i+1; j < n_old_npks; j++ ){
  229. n_dist = pn_locs[j] - ( i == -1 ? -1 : pn_locs[i] ); // lag-zero peak of autocorr is at index -1
  230. if ( n_dist > n_min_distance || n_dist < -n_min_distance )
  231. pn_locs[(*pn_npks)++] = pn_locs[j];
  232. }
  233. }
  234. // Resort indices longo ascending order
  235. maxim_sort_ascend( pn_locs, *pn_npks );
  236. }
  237. void maxim_sort_ascend(int32_t *pn_x,int32_t n_size)
  238. /**
  239. * \brief Sort array
  240. * \par Details
  241. * Sort array in ascending order (insertion sort algorithm)
  242. *
  243. * \retval None
  244. */
  245. {
  246. int32_t i, j, n_temp;
  247. for (i = 1; i < n_size; i++) {
  248. n_temp = pn_x[i];
  249. for (j = i; j > 0 && n_temp < pn_x[j-1]; j--)
  250. pn_x[j] = pn_x[j-1];
  251. pn_x[j] = n_temp;
  252. }
  253. }
  254. void maxim_sort_indices_descend(int32_t *pn_x, int32_t *pn_indx, int32_t n_size)
  255. /**
  256. * \brief Sort indices
  257. * \par Details
  258. * Sort indices according to descending order (insertion sort algorithm)
  259. *
  260. * \retval None
  261. */
  262. {
  263. int32_t i, j, n_temp;
  264. for (i = 1; i < n_size; i++) {
  265. n_temp = pn_indx[i];
  266. for (j = i; j > 0 && pn_x[n_temp] > pn_x[pn_indx[j-1]]; j--)
  267. pn_indx[j] = pn_indx[j-1];
  268. pn_indx[j] = n_temp;
  269. }
  270. }

algorithm.h

  1. #ifndef ALGORITHM_H_
  2. #define ALGORITHM_H_
  3. #include "main.h"
  4. #define true 1
  5. #define false 0
  6. #define FS 100
  7. #define BUFFER_SIZE (FS* 5)
  8. #define HR_FIFO_SIZE 7
  9. #define MA4_SIZE 4 // DO NOT CHANGE
  10. #define HAMMING_SIZE 5// DO NOT CHANGE
  11. #define min(x,y) ((x) < (y) ? (x) : (y))
  12. const uint16_t auw_hamm[31]={ 41, 276, 512, 276, 41 }; //Hamm= long16(512* hamming(5)');
  13. //uch_spo2_table is computed as -45.060*ratioAverage* ratioAverage + 30.354 *ratioAverage + 94.845 ;
  14. const uint8_t uch_spo2_table[184]={ 95, 95, 95, 96, 96, 96, 97, 97, 97, 97, 97, 98, 98, 98, 98, 98, 99, 99, 99, 99,
  15. 99, 99, 99, 99, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
  16. 100, 100, 100, 100, 99, 99, 99, 99, 99, 99, 99, 99, 98, 98, 98, 98, 98, 98, 97, 97,
  17. 97, 97, 96, 96, 96, 96, 95, 95, 95, 94, 94, 94, 93, 93, 93, 92, 92, 92, 91, 91,
  18. 90, 90, 89, 89, 89, 88, 88, 87, 87, 86, 86, 85, 85, 84, 84, 83, 82, 82, 81, 81,
  19. 80, 80, 79, 78, 78, 77, 76, 76, 75, 74, 74, 73, 72, 72, 71, 70, 69, 69, 68, 67,
  20. 66, 66, 65, 64, 63, 62, 62, 61, 60, 59, 58, 57, 56, 56, 55, 54, 53, 52, 51, 50,
  21. 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 31, 30, 29,
  22. 28, 27, 26, 25, 23, 22, 21, 20, 19, 17, 16, 15, 14, 12, 11, 10, 9, 7, 6, 5,
  23. 3, 2, 1 } ;
  24. static int32_t an_dx[ BUFFER_SIZE-MA4_SIZE]; // delta
  25. static int32_t an_x[ BUFFER_SIZE]; //ir
  26. static int32_t an_y[ BUFFER_SIZE]; //red
  27. void maxim_heart_rate_and_oxygen_saturation(uint32_t *pun_ir_buffer , int32_t n_ir_buffer_length, uint32_t *pun_red_buffer , int32_t *pn_spo2, int8_t *pch_spo2_valid , int32_t *pn_heart_rate , int8_t *pch_hr_valid);
  28. void maxim_find_peaks( int32_t *pn_locs, int32_t *pn_npks, int32_t *pn_x, int32_t n_size, int32_t n_min_height, int32_t n_min_distance, int32_t n_max_num );
  29. void maxim_peaks_above_min_height( int32_t *pn_locs, int32_t *pn_npks, int32_t *pn_x, int32_t n_size, int32_t n_min_height );
  30. void maxim_remove_close_peaks( int32_t *pn_locs, int32_t *pn_npks, int32_t *pn_x, int32_t n_min_distance );
  31. void maxim_sort_ascend( int32_t *pn_x, int32_t n_size );
  32. void maxim_sort_indices_descend( int32_t *pn_x, int32_t *pn_indx, int32_t n_size);
  33. #endif /* ALGORITHM_H_ */

I2C.c

  1. #include "myiic.h"
  2. #include "stm32f1xx_hal.h"
  3. //产生IIC起始信号
  4. void I2C_delay(void)
  5. {
  6. uint16_t i=60; //Set delay time value
  7. while(i)
  8. {
  9. i--;
  10. }
  11. }
  12. void IIC_Start(void)
  13. {
  14. SDA_H;
  15. I2C_delay();
  16. SCL_H;
  17. I2C_delay();
  18. SDA_L;
  19. I2C_delay();
  20. SCL_L;
  21. I2C_delay();
  22. }
  23. //产生IIC停止信号
  24. void IIC_Stop(void)
  25. {
  26. SCL_L;
  27. I2C_delay();
  28. SDA_L;
  29. I2C_delay();
  30. SCL_H;
  31. I2C_delay();
  32. SDA_H;
  33. I2C_delay();
  34. }
  35. //等待应答信号到来
  36. //返回值:1,接收应答失败
  37. // 0,接收应答成功
  38. uint8_t IIC_Wait_Ack(void)
  39. {
  40. uint8_t re;
  41. SCL_L;
  42. I2C_delay();
  43. SDA_H;
  44. I2C_delay();
  45. SCL_H;
  46. I2C_delay();
  47. if(SDA_read)
  48. {
  49. re=1;
  50. }
  51. else re=0;
  52. SCL_L;
  53. return re;
  54. // uint8_t re;
  55. // SDA_L; /* CPU释放SDA总线 */ //让它必须应答 自己修改的
  56. // I2C_delay();
  57. // SCL_H; /* CPU驱动SCL = 1, 此时器件会返回ACK应答 */
  58. // I2C_delay();
  59. //
  60. // re = SDA_read;/* CPU读取SDA口线状态 */
  61. // SCL_L;
  62. // I2C_delay();
  63. // return re;
  64. }
  65. //产生ACK应答
  66. void IIC_Ack(void)
  67. {
  68. SCL_L;
  69. I2C_delay();
  70. SDA_L;
  71. I2C_delay();
  72. SCL_H;
  73. I2C_delay();
  74. SCL_L;
  75. I2C_delay();
  76. }
  77. //不产生ACK应答
  78. void IIC_NAck(void)
  79. {
  80. SCL_L;
  81. I2C_delay();
  82. SDA_H;
  83. I2C_delay();
  84. SCL_H;
  85. I2C_delay();
  86. SCL_L;
  87. I2C_delay();
  88. }
  89. //IIC发送一个字节
  90. //返回从机有无应答
  91. //1,有应答
  92. //0,无应答
  93. void IIC_Send_Byte(uint8_t SendByte)
  94. {
  95. uint8_t i=8;
  96. while(i--)
  97. {
  98. SCL_L;
  99. I2C_delay();
  100. if((SendByte&0x80))
  101. {
  102. SDA_H;
  103. }
  104. else
  105. {
  106. SDA_L;
  107. }
  108. SendByte<<=1;
  109. I2C_delay();
  110. SCL_H;
  111. I2C_delay();
  112. }
  113. SCL_L;
  114. }
  115. //读1个字节,ack=1时,发送ACK,ack=0,发送nACK
  116. uint8_t IIC_Read_Byte(unsigned char ack)
  117. {
  118. unsigned char i=8,receive=0;
  119. SDA_H;
  120. while(i--)
  121. {
  122. receive<<=1;
  123. SCL_L;
  124. I2C_delay();
  125. SCL_H;
  126. I2C_delay();
  127. if(SDA_read)
  128. {
  129. receive|=0x01;
  130. }
  131. }
  132. SCL_L;
  133. if (!ack)
  134. IIC_NAck();//发送nACK
  135. else
  136. IIC_Ack(); //发送ACK
  137. return receive;
  138. }
  139. void IIC_WriteBytes(uint8_t WriteAddr,uint8_t* data,uint8_t dataLength)
  140. {
  141. uint8_t i;
  142. IIC_Start();
  143. IIC_Send_Byte(WriteAddr); //发送写命令
  144. IIC_Wait_Ack();
  145. for(i=0;i<dataLength;i++)
  146. {
  147. IIC_Send_Byte(data[i]);
  148. IIC_Wait_Ack();
  149. }
  150. IIC_Stop();//产生一个停止条件
  151. I2C_delay();
  152. }
  153. void IIC_ReadBytes(uint8_t deviceAddr, uint8_t writeAddr,uint8_t* data,uint8_t dataLength)
  154. {
  155. uint8_t i;
  156. IIC_Start();
  157. IIC_Send_Byte(deviceAddr); //发送写命令
  158. IIC_Wait_Ack();
  159. IIC_Send_Byte(writeAddr);
  160. IIC_Wait_Ack();
  161. IIC_Send_Byte(deviceAddr|0X01);//进入接收模式
  162. IIC_Wait_Ack();
  163. for(i=0;i<dataLength-1;i++)
  164. {
  165. data[i] = IIC_Read_Byte(1);
  166. }
  167. data[dataLength-1] = IIC_Read_Byte(0);
  168. IIC_Stop();//产生一个停止条件
  169. I2C_delay();
  170. }
  171. void IIC_Read_One_Byte(uint8_t daddr,uint8_t addr,uint8_t* data)
  172. {
  173. IIC_Start();
  174. IIC_Send_Byte(daddr); //发送写命令
  175. IIC_Wait_Ack();
  176. IIC_Send_Byte(addr);//发送地址
  177. IIC_Wait_Ack();
  178. IIC_Start();
  179. IIC_Send_Byte(daddr|0X01);//进入接收模式
  180. IIC_Wait_Ack();
  181. *data = IIC_Read_Byte(0);
  182. IIC_Stop();//产生一个停止条件
  183. }
  184. void IIC_Write_One_Byte(uint8_t daddr,uint8_t addr,uint8_t data)
  185. {
  186. IIC_Start();
  187. IIC_Send_Byte(daddr); //发送写命令
  188. IIC_Wait_Ack();
  189. IIC_Send_Byte(addr);//发送地址
  190. IIC_Wait_Ack();
  191. IIC_Send_Byte(data); //发送字节
  192. IIC_Wait_Ack();
  193. IIC_Stop();//产生一个停止条件
  194. I2C_delay();
  195. }

main.c

  1. int main(void)
  2. {
  3. /* USER CODE BEGIN 1 */
  4. /* USER CODE END 1 */
  5. /* MCU Configuration--------------------------------------------------------*/
  6. /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  7. HAL_Init();
  8. /* USER CODE BEGIN Init */
  9. /* USER CODE END Init */
  10. /* Configure the system clock */
  11. SystemClock_Config();
  12. /* USER CODE BEGIN SysInit */
  13. /* USER CODE END SysInit */
  14. /* Initialize all configured peripherals */
  15. MX_GPIO_Init();
  16. MX_USART3_UART_Init();
  17. /* USER CODE BEGIN 2 */
  18. OLED_Init(); //初始化OLED
  19. OLED_Clear() ;
  20. max30102_init();
  21. MAX30102_data_set();
  22. //wait until the user presses a key
  23. // while(pc.readable()==0)
  24. // {
  25. // pc.printf("\x1B[2J"); //clear terminal program screen
  26. // pc.printf("Press any key to start conversion\n\r");
  27. // wait(1);
  28. // }
  29. // uch_dummy=getchar();
  30. /* USER CODE END 2 */
  31. /* Infinite loop */
  32. /* USER CODE BEGIN WHILE */
  33. // HAL_GPIO_WritePin(GPIOB,MAX30102_INT_Pin,SET);
  34. while (1)
  35. {
  36. /* USER CODE END WHILE */
  37. /* USER CODE BEGIN 3 */
  38. MAX30102_get(&HR,&SPO2);
  39. OLED_ShowNum(30,0,HR,3,16);
  40. OLED_ShowNum(60,0,SPO2,3,16);
  41. }
  42. /* USER CODE END 3 */
  43. }

OLED

  这个大家可以直接去上网购物平台搜索OLED,一般商家都会有现成的代码可以使用,比如我这里用的则是中景园的OLED代码,大家也可以直接上购物平台查。

四、效果展示

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/436932
推荐阅读
相关标签
  

闽ICP备14008679号