硬件:ADI评估板-MAX78000FTHR#
ADI脉搏血氧计传感器-MAXREFDES117#
显示屏- oled 128*64
通过 心率传感器获取数据,经过解析,显示到oled上,oled增加了心率数据的实时曲线。
增加一个回调函数定义,配置传感器的时候,传递回调函数指针,函数用于获取实时心率数据。
其中oled 和心率传感器使用iic 接口,控制板有一个iic接口,所以实现的方案里,这两个设备都挂在iic总线上。
同时实现点亮oled和读取心率传感器的操作。
curve_dp cb_func = NULL; void MAX30102_Config(curve_dp *cb) { cb_func = cb; MAX30102_reset(); enable_temp(); max30102_write_reg(REG_INTR_ENABLE_1,0xc0); //INTR setting max30102_write_reg(REG_INTR_ENABLE_2,0x00);// max30102_write_reg(REG_FIFO_WR_PTR,0x00);//FIFO_WR_PTR[4:0] max30102_write_reg(REG_OVF_COUNTER,0x00);//OVF_COUNTER[4:0] max30102_write_reg(REG_FIFO_RD_PTR,0x00);//FIFO_RD_PTR[4:0] max30102_write_reg(REG_FIFO_CONFIG,0x0f);//sample avg = 1, fifo rollover=false, fifo almost full = 17 max30102_write_reg(REG_MODE_CONFIG,0x03);//0x02 for Red only, 0x03 for SpO2 mode 0x07 multimode LED max30102_write_reg(REG_SPO2_CONFIG,0x27); // SPO2_ADC range = 4096nA, SPO2 sample rate (50 Hz), LED pulseWidth (400uS) max30102_write_reg(REG_LED1_PA,0x32);//Choose value for ~ 10mA for LED1 max30102_write_reg(REG_LED2_PA,0x32);// Choose value for ~ 10mA for LED2 max30102_write_reg(REG_PILOT_PA,0x7f);// Choose value for ~ 25mA for Pilot LED printf("REG_PART_ID: %x\r\n",max30102_read_reg(REG_PART_ID)); printf("REG_REV_ID: %x\r\n",max30102_read_reg(REG_REV_ID)); }
读取fifo数据寄存器,并间隔20个数据上报一次实时心率数据。
#define CAL_UNIT 20 uint16_t cal_num=CAL_UNIT; uint32_t un_min=0xffffff ,un_max=0,un_prev_data=0; void max30102_read_fifo(uint32_t *fifo_red,uint32_t *fifo_ir) { uint8_t ach_i2c_data[6] = {0}; //read and clear status register max30102_read_reg(REG_INTR_STATUS_1); max30102_read_reg(REG_INTR_STATUS_2); max30102_read_data(REG_FIFO_DATA,ach_i2c_data,6); *fifo_red = (long)((long)((long)ach_i2c_data[0]&0x03)<<16) | (long)ach_i2c_data[1]<<8 | (long)ach_i2c_data[2]; //将值合并得到实际数字,数组400-500为新读取数据 *fifo_ir = (long)((long)((long)ach_i2c_data[3]&0x03)<<16) | (long)ach_i2c_data[4]<<8 | (long)ach_i2c_data[5]; //将值合并得到实际数字,数组400-500为新读取数据 *fifo_red&=0x03FFFF; //Mask MSB [23:18] *fifo_ir&=0x03FFFF; //Mask MSB [23:18] if(!cal_num){ if(cb_func) { uint32_t temp = min(un_max, *fifo_red); temp = max(un_min, *fifo_red); float_t bili = (float_t)((temp-un_min)*1.0/(un_max - un_min)); //printf("bili[%f][%d:%d:%d]\n",bili,un_max,temp,un_min); cb_func(bili); } cal_num = CAL_UNIT; }else{ cal_num--; }
显示试试心率曲线
void curve_red_data(float bili) { u8g2_t *u8g2 = &u8g2_st; u8g2_SetDrawColor(&u8g2_st,0); u8g2_DrawBox(&u8g2_st,0, 40, 128, 24); u8g2_SetDrawColor(&u8g2_st,1); // u8g2_ClearBuffer(u8g2); uint8_t psi; uint8_t curve_dt = bili*24 + 40; curve_dt = min(curve_dt, 64); curve_dt = max(curve_dt, 40); data_str[bujin] = curve_dt; //log_2("curve_dp [%f]data[%d]curve_dt[%d]bujin[%d]",bili,data_str[bujin],curve_dt,bujin); bujin = 128 <= (bujin+1) ?0 : (bujin+1); for(int iii = 0; iii < 128; iii++){ psi = iii+bujin; if(psi >= 128) psi -= 128; u8g2_DrawPixel(u8g2, iii, data_str[psi]); // } // uint8_t data_str[10]; // u8g2_SetFont(u8g2, u8g2_font_wqy15_t_gb2312); /*字库选择*/ // sprintf((char*)data_str,"%d",curve_dt); // u8g2_DrawUTF8(u8g2, 100, 35, data_str); u8g2_SendBuffer(u8g2); }
解析心率和血氧样本,并使用官方算法进行解析。
图数据有效,则返回有效数据标识。
uint8_t get_data_show(uint32_t *hr_rate,uint32_t *spo2_rate) { // 读取和计算max30102数据,总体用缓存的500组数据分析,实际每读取100组新数据分析一次 un_min = 0x3FFFF; un_max = 0; uint32_t count = 500; //采样点数 uint32_t i; float f_temp; // 将前100组样本转储到内存中(实际没有),并将后400组样本移到顶部,将100-500缓存数据移位到0-400 for(i = 100; i < 500; i++){ aun_red_buffer[i - 100] = aun_red_buffer[i]; // 将100-500缓存数据移位到0-400 aun_ir_buffer[i - 100] = aun_ir_buffer[i]; // 将100-500缓存数据移位到0-400 // 更新信号的最小值和最大值 un_min = min(un_min, aun_red_buffer[i]); un_max = max(un_max, aun_red_buffer[i]); } // 在计算心率前取100组样本,取的数据放在400-500缓存数组中 for(i = 400; i < 500; i++){ un_prev_data = aun_red_buffer[i - 1]; // 临时记录上一次读取数据 while(!(max30102_read_reg(REG_INTR_STATUS_1) & 0x40)){ } max30102_read_fifo(&aun_red_buffer[i], &aun_ir_buffer[i]); //read from MAX30102 FIFO2 if(aun_red_buffer[i] > un_prev_data){ // 用新获取的一个数值与上一个数值对比 f_temp = aun_red_buffer[i] - un_prev_data; f_temp /= (un_max - un_min); f_temp *= MAX_BRIGHTNESS; // 公式(心率曲线)=(新数值-旧数值)/(最大值-最小值)*255 n_brightness -= (int)f_temp; if(n_brightness < 0) n_brightness = 0; }else{ f_temp = un_prev_data - aun_red_buffer[i]; f_temp /= (un_max - un_min); f_temp *= MAX_BRIGHTNESS; // 公式(心率曲线)=(旧数值-新数值)/(最大值-最小值)*255 n_brightness += (int)f_temp; if(n_brightness > MAX_BRIGHTNESS) n_brightness = MAX_BRIGHTNESS; } } maxim_heart_rate_and_oxygen_saturation(aun_ir_buffer, 500, aun_red_buffer, &n_sp02, &ch_spo2_valid, &n_heart_rate, &ch_hr_valid); // 传入500个心率和血氧数据计算传感器检测结论,反馈心率和血氧测试结果 *hr_rate = n_heart_rate; *spo2_rate = n_sp02; if((1 == ch_hr_valid) && (1 == ch_spo2_valid) && (n_heart_rate < 120) && (n_sp02 < 101)) { printf("HeartRate=%i, BloodOxyg=%i\r\n", n_heart_rate, n_sp02); return 1; } return 0; }
展示心率和血氧
void showfirstpage(u8g2_t *u8g2) { u8g2_SetFont(u8g2, u8g2_font_wqy12_t_gb2312); /*字库选择*/ u8g2_DrawUTF8(u8g2, 20, 15, "脉搏"); u8g2_DrawUTF8(u8g2, 84, 15, "血氧"); u8g2_DrawHLine(u8g2, 0, 20, 128); u8g2_SendBuffer(u8g2); //u8g2_ClearBuffer(u8g2); log("showfirstpage done"); } void showdata(u8g2_t *u8g2,uint32_t hr_rate,uint32_t spo2_rate) { uint8_t data_str[10]; u8g2_SetFont(u8g2, u8g2_font_wqy15_t_gb2312); /*字库选择*/ //u8g2_font_wqy16_t_gb2312 sprintf((char*)data_str,"%03d",hr_rate); u8g2_DrawUTF8(u8g2, 20, 35, data_str); sprintf((char*)data_str,"%03d",spo2_rate); u8g2_DrawUTF8(u8g2, 85, 35, data_str); u8g2_SendBuffer(u8g2); }
效果展示
代码
-----------------------------------------------------------------------------------------------------------------------------------------
字库自定义
使用github上开源的工具:larryli/u8g2_wqy: 适合 u8g2 的中文字体,采用文泉驿点阵宋体作为源本,提供 12x12、13x13、14x14、15x15 和 16x16 点阵字库。
编译text文件,添加自己需要的字符。
基于源码提供的gb2312.txt,删除不需要的汉字,保留需要的汉字。
可以使用git终端里提供的iconv工具,此工具提供完整的编码。
使用脚本genmaps.sh,生成需要的maps文件,里面是我们需要的字符unicode编码,并且进行了格式化。
再运行脚本 build.sh,生成我们需要的c文件,位置位于src里。
把c文件的数组复制到项目里的fonts.c文件里,就可以使用了。
问题:
1,oled和传感器使用同一个iic链接,数据数据时候,传感器读取一次,立马卡住,死循环在iic驱动代码里,读不到新数据,一直循环。调试堆栈,如下图
方案:
因为能够读取一次传感器数据,所以通信问题不大,可能总线锁死,导致无法继续读取,传感器数据也无法发送过来。
在读iic的接口里,增加一个停止接口,主动释放一下总线,问题没了